00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "gui.h"
00014 #include "openttd.h"
00015 #include "debug.h"
00016 #include "roadveh.h"
00017 #include "ship.h"
00018 #include "spritecache.h"
00019 #include "landscape.h"
00020 #include "timetable.h"
00021 #include "viewport_func.h"
00022 #include "news_func.h"
00023 #include "command_func.h"
00024 #include "company_func.h"
00025 #include "vehicle_gui.h"
00026 #include "train.h"
00027 #include "aircraft.h"
00028 #include "newgrf_engine.h"
00029 #include "newgrf_sound.h"
00030 #include "newgrf_station.h"
00031 #include "group.h"
00032 #include "group_gui.h"
00033 #include "strings_func.h"
00034 #include "zoom_func.h"
00035 #include "functions.h"
00036 #include "date_func.h"
00037 #include "window_func.h"
00038 #include "vehicle_func.h"
00039 #include "autoreplace_func.h"
00040 #include "autoreplace_gui.h"
00041 #include "station_base.h"
00042 #include "ai/ai.hpp"
00043 #include "core/smallmap_type.hpp"
00044 #include "depot_func.h"
00045 #include "network/network.h"
00046 #include "core/pool_func.hpp"
00047 #include "economy_base.h"
00048 #include "articulated_vehicles.h"
00049 #include "roadstop_base.h"
00050 #include "infrastructure_func.h"
00051
00052 #include "table/sprites.h"
00053 #include "table/strings.h"
00054
00055 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00056
00057 VehicleID _vehicle_id_ctr_day;
00058 const Vehicle *_place_clicked_vehicle;
00059 VehicleID _new_vehicle_id;
00060 uint16 _returned_refit_capacity;
00061 byte _age_cargo_skip_counter;
00062
00063
00064
00065 VehiclePool _vehicle_pool("Vehicle");
00066 INSTANTIATE_POOL_METHODS(Vehicle)
00067
00068
00072 bool Vehicle::NeedsAutorenewing(const Company *c) const
00073 {
00074
00075
00076
00077
00078 assert(c == Company::Get(this->owner));
00079
00080 if (!c->settings.engine_renew) return false;
00081 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00082 if (this->age == 0) return false;
00083
00084 return true;
00085 }
00086
00087 void VehicleServiceInDepot(Vehicle *v)
00088 {
00089 v->date_of_last_service = _date;
00090 v->breakdowns_since_last_service = 0;
00091 v->reliability = Engine::Get(v->engine_type)->reliability;
00092 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00093 }
00094
00095 bool Vehicle::NeedsServicing() const
00096 {
00097
00098
00099 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00100
00101
00102 const Company *c = Company::Get(this->owner);
00103 if (c->settings.vehicle.servint_ispercent ?
00104 (this->reliability >= Engine::Get(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00105 (this->date_of_last_service + this->service_interval >= _date)) {
00106 return false;
00107 }
00108
00109
00110
00111 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00112 _settings_game.difficulty.vehicle_breakdowns != 0) {
00113 return true;
00114 }
00115
00116
00117
00118
00119 bool pending_replace = false;
00120 Money needed_money = c->settings.engine_renew_money;
00121 if (needed_money > c->money) return false;
00122
00123 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00124 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
00125
00126
00127 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00128
00129
00130 uint32 available_cargo_types, union_mask;
00131 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00132
00133 if (union_mask != 0) {
00134 CargoID cargo_type;
00135
00136 if (IsArticulatedVehicleCarryingDifferentCargos(v, &cargo_type)) continue;
00137
00138
00139 if (cargo_type != CT_INVALID) {
00140
00141 if (!HasBit(available_cargo_types, cargo_type)) continue;
00142 }
00143 }
00144
00145
00146
00147 pending_replace = true;
00148 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00149 if (needed_money > c->money) return false;
00150 }
00151
00152 return pending_replace;
00153 }
00154
00155 bool Vehicle::NeedsAutomaticServicing() const
00156 {
00157 if (_settings_game.order.gotodepot && VehicleHasDepotOrders(this)) return false;
00158 if (this->current_order.IsType(OT_LOADING)) return false;
00159 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00160 return NeedsServicing();
00161 }
00162
00163 uint Vehicle::Crash(bool flooded)
00164 {
00165 assert((this->vehstatus & VS_CRASHED) == 0);
00166 assert(this->Previous() == NULL);
00167
00168 uint pass = 0;
00169
00170 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00171 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00172 v->vehstatus |= VS_CRASHED;
00173 MarkSingleVehicleDirty(v);
00174 }
00175
00176
00177 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00178 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
00179 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00180 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00181
00182 return pass;
00183 }
00184
00185
00194 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00195 {
00196 const Engine *e = Engine::Get(engine);
00197 uint32 grfid = e->grffile->grfid;
00198 GRFConfig *grfconfig = GetGRFConfig(grfid);
00199
00200 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00201 SetBit(grfconfig->grf_bugs, bug_type);
00202 SetDParamStr(0, grfconfig->name);
00203 SetDParam(1, engine);
00204 ShowErrorMessage(part1, part2, 0, 0, true);
00205 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00206 }
00207
00208
00209 char buffer[512];
00210
00211 SetDParamStr(0, grfconfig->name);
00212 GetString(buffer, part1, lastof(buffer));
00213 DEBUG(grf, 0, "%s", buffer + 3);
00214
00215 SetDParam(1, engine);
00216 GetString(buffer, part2, lastof(buffer));
00217 DEBUG(grf, 0, "%s", buffer + 3);
00218 }
00219
00220 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00221 {
00222 byte z = *(byte*)data;
00223
00224 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00225 if (v->z_pos > z) return NULL;
00226
00227 _error_message = STR_ERROR_TRAIN_IN_THE_WAY + v->type;
00228 return v;
00229 }
00230
00231 bool EnsureNoVehicleOnGround(TileIndex tile)
00232 {
00233 byte z = GetTileMaxZ(tile);
00234 return !HasVehicleOnPos(tile, &z, &EnsureNoVehicleProcZ);
00235 }
00236
00238 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00239 {
00240 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00241 if (v == (const Vehicle *)data) return NULL;
00242
00243 _error_message = STR_ERROR_TRAIN_IN_THE_WAY + v->type;
00244 return v;
00245 }
00246
00254 bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00255 {
00256 return HasVehicleOnPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc) ||
00257 HasVehicleOnPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc);
00258 }
00259
00260
00261 Vehicle::Vehicle(VehicleType type)
00262 {
00263 this->type = type;
00264 this->coord.left = INVALID_COORD;
00265 this->group_id = DEFAULT_GROUP;
00266 this->fill_percent_te_id = INVALID_TE_ID;
00267 this->first = this;
00268 this->colourmap = PAL_NONE;
00269 }
00270
00275 byte VehicleRandomBits()
00276 {
00277 return GB(Random(), 0, 8);
00278 }
00279
00280
00281
00282 const int HASH_BITS = 7;
00283 const int HASH_SIZE = 1 << HASH_BITS;
00284 const int HASH_MASK = HASH_SIZE - 1;
00285 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00286 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00287
00288
00289
00290 const int HASH_RES = 0;
00291
00292 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00293
00294 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00295 {
00296 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00297 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00298 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00299 for (; v != NULL; v = v->next_new_hash) {
00300 Vehicle *a = proc(v, data);
00301 if (find_first && a != NULL) return a;
00302 }
00303 if (x == xu) break;
00304 }
00305 if (y == yu) break;
00306 }
00307
00308 return NULL;
00309 }
00310
00311
00323 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00324 {
00325 const int COLL_DIST = 6;
00326
00327
00328 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00329 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00330 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00331 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00332
00333 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00334 }
00335
00350 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00351 {
00352 VehicleFromPosXY(x, y, data, proc, false);
00353 }
00354
00366 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00367 {
00368 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00369 }
00370
00381 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00382 {
00383 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00384 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00385
00386 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00387 for (; v != NULL; v = v->next_new_hash) {
00388 if (v->tile != tile) continue;
00389
00390 Vehicle *a = proc(v, data);
00391 if (find_first && a != NULL) return a;
00392 }
00393
00394 return NULL;
00395 }
00396
00410 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00411 {
00412 VehicleFromPos(tile, data, proc, false);
00413 }
00414
00425 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00426 {
00427 return VehicleFromPos(tile, data, proc, true) != NULL;
00428 }
00429
00430
00431 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00432 {
00433 Vehicle **old_hash = v->old_new_hash;
00434 Vehicle **new_hash;
00435
00436 if (remove) {
00437 new_hash = NULL;
00438 } else {
00439 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00440 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00441 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00442 }
00443
00444 if (old_hash == new_hash) return;
00445
00446
00447 if (old_hash != NULL) {
00448 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = v->prev_new_hash;
00449 *v->prev_new_hash = v->next_new_hash;
00450 }
00451
00452
00453 if (new_hash != NULL) {
00454 v->next_new_hash = *new_hash;
00455 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = &v->next_new_hash;
00456 v->prev_new_hash = new_hash;
00457 *new_hash = v;
00458 }
00459
00460
00461 v->old_new_hash = new_hash;
00462 }
00463
00464 static Vehicle *_vehicle_position_hash[0x1000];
00465
00466 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00467 {
00468 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00469
00470 Vehicle **old_hash, **new_hash;
00471 int old_x = v->coord.left;
00472 int old_y = v->coord.top;
00473
00474 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00475 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00476
00477 if (old_hash == new_hash) return;
00478
00479
00480 if (old_hash != NULL) {
00481 if (v->next_hash != NULL) v->next_hash->prev_hash = v->prev_hash;
00482 *v->prev_hash = v->next_hash;
00483 }
00484
00485
00486 if (new_hash != NULL) {
00487 v->next_hash = *new_hash;
00488 if (v->next_hash != NULL) v->next_hash->prev_hash = &v->next_hash;
00489 v->prev_hash = new_hash;
00490 *new_hash = v;
00491 }
00492 }
00493
00494 void ResetVehiclePosHash()
00495 {
00496 Vehicle *v;
00497 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00498 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00499 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00500 }
00501
00502 void ResetVehicleColourMap()
00503 {
00504 Vehicle *v;
00505 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00506 }
00507
00512 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00513 static AutoreplaceMap _vehicles_to_autoreplace;
00514
00515 void InitializeVehicles()
00516 {
00517 _vehicle_pool.CleanPool();
00518 _cargo_payment_pool.CleanPool();
00519
00520 _age_cargo_skip_counter = 1;
00521
00522 _vehicles_to_autoreplace.Reset();
00523 ResetVehiclePosHash();
00524 }
00525
00526 uint CountVehiclesInChain(const Vehicle *v)
00527 {
00528 uint count = 0;
00529 do count++; while ((v = v->Next()) != NULL);
00530 return count;
00531 }
00532
00536 bool Vehicle::IsEngineCountable() const
00537 {
00538 switch (this->type) {
00539 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00540 case VEH_TRAIN:
00541 return !Train::From(this)->IsArticulatedPart() &&
00542 !Train::From(this)->IsRearDualheaded();
00543 case VEH_ROAD: return RoadVehicle::From(this)->IsRoadVehFront();
00544 case VEH_SHIP: return true;
00545 default: return false;
00546 }
00547 }
00548
00549 void Vehicle::PreDestructor()
00550 {
00551 if (CleaningPool()) return;
00552
00553 if (Station::IsValidID(this->last_station_visited)) {
00554 Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00555
00556 HideFillingPercent(&this->fill_percent_te_id);
00557
00558 delete this->cargo_payment;
00559 }
00560
00561 if (this->IsEngineCountable()) {
00562 Company::Get(this->owner)->num_engines[this->engine_type]--;
00563 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00564
00565 DeleteGroupHighlightOfVehicle(this);
00566 if (Group::IsValidID(this->group_id)) Group::Get(this->group_id)->num_engines[this->engine_type]--;
00567 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00568 }
00569
00570 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00571 Aircraft *a = Aircraft::From(this);
00572 Station *st = GetTargetAirportIfValid(a);
00573 if (st != NULL) {
00574 const AirportFTA *layout = st->Airport()->layout;
00575 CLRBITS(st->airport_flags, layout[a->previous_pos].block | layout[a->pos].block);
00576 }
00577 }
00578
00579
00580 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00581 RoadVehicle *v = RoadVehicle::From(this);
00582 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00583
00584 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00585 }
00586 }
00587
00588 if (this->Previous() == NULL) {
00589 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00590 }
00591
00592 if (this->IsPrimaryVehicle()) {
00593 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00594 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00595 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00596 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00597 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00598 SetWindowDirty(WC_COMPANY, this->owner);
00599 }
00600 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00601
00602 this->cargo.Truncate(0);
00603 DeleteVehicleOrders(this);
00604 DeleteDepotHighlightOfVehicle(this);
00605
00606 extern void StopGlobalFollowVehicle(const Vehicle *v);
00607 StopGlobalFollowVehicle(this);
00608
00609 ReleaseDisastersTargetingVehicle(this->index);
00610 }
00611
00612 Vehicle::~Vehicle()
00613 {
00614 free(this->name);
00615
00616 if (CleaningPool()) return;
00617
00618
00619
00620 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00621
00622 Vehicle *v = this->Next();
00623 this->SetNext(NULL);
00624
00625 delete v;
00626
00627 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00628 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00629 }
00630
00634 void VehicleEnteredDepotThisTick(Vehicle *v)
00635 {
00636
00637 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00638
00639
00640
00641
00642
00643
00644 v->vehstatus |= VS_STOPPED;
00645 }
00646
00652 static void RunVehicleDayProc()
00653 {
00654 if (_game_mode != GM_NORMAL) return;
00655
00656
00657 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00658 Vehicle *v = Vehicle::Get(i);
00659 if (v == NULL) continue;
00660
00661
00662 if ((v->day_counter & 0x1F) == 0) {
00663 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00664 if (callback != CALLBACK_FAILED) {
00665 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00666 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00667 }
00668 }
00669
00670
00671 v->OnNewDay();
00672 }
00673 }
00674
00675 void CallVehicleTicks()
00676 {
00677 _vehicles_to_autoreplace.Clear();
00678
00679 _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
00680
00681 RunVehicleDayProc();
00682
00683 Station *st;
00684 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00685
00686 Vehicle *v;
00687 FOR_ALL_VEHICLES(v) {
00688
00689 if (!v->Tick()) {
00690 assert(Vehicle::Get(vehicle_index) == NULL);
00691 continue;
00692 }
00693
00694 assert(Vehicle::Get(vehicle_index) == v);
00695
00696 switch (v->type) {
00697 default: break;
00698
00699 case VEH_TRAIN:
00700 case VEH_ROAD:
00701 case VEH_AIRCRAFT:
00702 case VEH_SHIP:
00703 if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
00704
00705 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00706 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00707 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsRoadVehFront()) continue;
00708
00709 v->motion_counter += v->cur_speed;
00710
00711 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00712
00713
00714 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00715 }
00716 }
00717
00718 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00719 v = it->first;
00720
00721 _current_company = v->owner;
00722
00723
00724
00725
00726 if (it->second) v->vehstatus &= ~VS_STOPPED;
00727
00728
00729 int x = v->x_pos;
00730 int y = v->y_pos;
00731 int z = v->z_pos;
00732
00733 const Company *c = Company::Get(_current_company);
00734 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00735 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00736 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00737
00738 if (!IsLocalCompany()) continue;
00739
00740 if (res.Succeeded()) {
00741 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00742 continue;
00743 }
00744
00745 StringID error_message = res.GetErrorMessage();
00746 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00747
00748 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00749
00750 StringID message;
00751 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00752 message = error_message;
00753 } else {
00754 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00755 }
00756
00757 SetDParam(0, v->index);
00758 SetDParam(1, error_message);
00759 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00760 }
00761
00762 _current_company = OWNER_NONE;
00763 }
00764
00765 static void DoDrawVehicle(const Vehicle *v)
00766 {
00767 SpriteID image = v->cur_image;
00768 SpriteID pal = PAL_NONE;
00769
00770 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00771
00772 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00773 v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00774 }
00775
00776 void ViewportAddVehicles(DrawPixelInfo *dpi)
00777 {
00778
00779 const int l = dpi->left;
00780 const int r = dpi->left + dpi->width;
00781 const int t = dpi->top;
00782 const int b = dpi->top + dpi->height;
00783
00784
00785 int xl, xu, yl, yu;
00786
00787 if (dpi->width + 70 < (1 << (7 + 6))) {
00788 xl = GB(l - 70, 7, 6);
00789 xu = GB(r, 7, 6);
00790 } else {
00791
00792 xl = 0;
00793 xu = 0x3F;
00794 }
00795
00796 if (dpi->height + 70 < (1 << (6 + 6))) {
00797 yl = GB(t - 70, 6, 6) << 6;
00798 yu = GB(b, 6, 6) << 6;
00799 } else {
00800
00801 yl = 0;
00802 yu = 0x3F << 6;
00803 }
00804
00805 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00806 for (int x = xl;; x = (x + 1) & 0x3F) {
00807 const Vehicle *v = _vehicle_position_hash[x + y];
00808
00809 while (v != NULL) {
00810 if (!(v->vehstatus & VS_HIDDEN) &&
00811 l <= v->coord.right &&
00812 t <= v->coord.bottom &&
00813 r >= v->coord.left &&
00814 b >= v->coord.top) {
00815 DoDrawVehicle(v);
00816 }
00817 v = v->next_hash;
00818 }
00819
00820 if (x == xu) break;
00821 }
00822
00823 if (y == yu) break;
00824 }
00825 }
00826
00827 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00828 {
00829 Vehicle *found = NULL, *v;
00830 uint dist, best_dist = UINT_MAX;
00831
00832 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00833
00834 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00835 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00836
00837 FOR_ALL_VEHICLES(v) {
00838 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00839 x >= v->coord.left && x <= v->coord.right &&
00840 y >= v->coord.top && y <= v->coord.bottom) {
00841
00842 dist = max(
00843 abs(((v->coord.left + v->coord.right) >> 1) - x),
00844 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00845 );
00846
00847 if (dist < best_dist) {
00848 found = v;
00849 best_dist = dist;
00850 }
00851 }
00852 }
00853
00854 return found;
00855 }
00856
00857 void DecreaseVehicleValue(Vehicle *v)
00858 {
00859 v->value -= v->value >> 8;
00860 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00861 }
00862
00863 static const byte _breakdown_chance[64] = {
00864 3, 3, 3, 3, 3, 3, 3, 3,
00865 4, 4, 5, 5, 6, 6, 7, 7,
00866 8, 8, 9, 9, 10, 10, 11, 11,
00867 12, 13, 13, 13, 13, 14, 15, 16,
00868 17, 19, 21, 25, 28, 31, 34, 37,
00869 40, 44, 48, 52, 56, 60, 64, 68,
00870 72, 80, 90, 100, 110, 120, 130, 140,
00871 150, 170, 190, 210, 230, 250, 250, 250,
00872 };
00873
00874 void CheckVehicleBreakdown(Vehicle *v)
00875 {
00876 int rel, rel_old;
00877
00878
00879 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
00880 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00881
00882 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
00883 _settings_game.difficulty.vehicle_breakdowns < 1 ||
00884 v->cur_speed < 5 || _game_mode == GM_MENU) {
00885 return;
00886 }
00887
00888 uint32 r = Random();
00889
00890
00891 int chance = v->breakdown_chance + 1;
00892 if (Chance16I(1, 25, r)) chance += 25;
00893 v->breakdown_chance = min(255, chance);
00894
00895
00896 rel = v->reliability;
00897 if (v->type == VEH_SHIP) rel += 0x6666;
00898
00899
00900 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
00901
00902
00903 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
00904 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
00905 v->breakdown_delay = GB(r, 24, 7) + 0x80;
00906 v->breakdown_chance = 0;
00907 }
00908 }
00909
00910 void AgeVehicle(Vehicle *v)
00911 {
00912 if (v->age < 65535) v->age++;
00913
00914 int age = v->age - v->max_age;
00915 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
00916 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
00917 v->reliability_spd_dec <<= 1;
00918 }
00919
00920 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00921
00922
00923 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
00924
00925
00926 if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
00927
00928 StringID str;
00929 if (age == -DAYS_IN_LEAP_YEAR) {
00930 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
00931 } else if (age == 0) {
00932 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
00933 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
00934 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
00935 } else {
00936 return;
00937 }
00938
00939 SetDParam(0, v->index);
00940 AddVehicleNewsItem(str, NS_ADVICE, v->index);
00941 }
00942
00949 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
00950 {
00951 int count = 0;
00952 int max = 0;
00953 int cars = 0;
00954 int unloading = 0;
00955 bool loading = false;
00956
00957 const Vehicle *u = v;
00958 const Station *st = v->last_station_visited != INVALID_STATION ? Station::Get(v->last_station_visited) : NULL;
00959
00960
00961 for (; v != NULL; v = v->Next()) {
00962 count += v->cargo.Count();
00963 max += v->cargo_cap;
00964 if (v->cargo_cap != 0 && colour != NULL) {
00965 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
00966 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
00967 cars++;
00968 }
00969 }
00970
00971 if (colour != NULL) {
00972 if (unloading == 0 && loading) {
00973 *colour = STR_PERCENT_UP;
00974 } else if (cars == unloading || !loading) {
00975 *colour = STR_PERCENT_DOWN;
00976 } else {
00977 *colour = STR_PERCENT_UP_DOWN;
00978 }
00979 }
00980
00981
00982 if (max == 0) return 100;
00983
00984
00985 return (count * 100) / max;
00986 }
00987
00988 void VehicleEnterDepot(Vehicle *v)
00989 {
00990
00991 assert(v == v->First());
00992
00993 switch (v->type) {
00994 case VEH_TRAIN: {
00995 Train *t = Train::From(v);
00996 SetWindowClassesDirty(WC_TRAINS_LIST);
00997
00998 SetDepotReservation(t->tile, false);
00999 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01000
01001 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01002 t->time_counter = 0;
01003 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01004 TrainConsistChanged(t, true);
01005 break;
01006 }
01007
01008 case VEH_ROAD:
01009 SetWindowClassesDirty(WC_ROADVEH_LIST);
01010 break;
01011
01012 case VEH_SHIP:
01013 SetWindowClassesDirty(WC_SHIPS_LIST);
01014 Ship::From(v)->state = TRACK_BIT_DEPOT;
01015 RecalcShipStuff(v);
01016 break;
01017
01018 case VEH_AIRCRAFT:
01019 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01020 HandleAircraftEnterHangar(Aircraft::From(v));
01021 break;
01022 default: NOT_REACHED();
01023 }
01024
01025 if (v->type != VEH_TRAIN) {
01026
01027
01028 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01029 }
01030 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01031
01032 v->vehstatus |= VS_HIDDEN;
01033 v->cur_speed = 0;
01034
01035 VehicleServiceInDepot(v);
01036
01037 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01038
01039 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01040 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01041
01042 const Order *real_order = v->GetOrder(v->cur_order_index);
01043 Order t = v->current_order;
01044 v->current_order.MakeDummy();
01045
01046
01047
01048 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01049 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01050 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01051
01052 return;
01053 }
01054
01055 if (t.IsRefit()) {
01056 _current_company = v->owner;
01057 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01058
01059 if (CmdFailed(cost)) {
01060 _vehicles_to_autoreplace[v] = false;
01061 if (v->owner == _local_company) {
01062
01063 SetDParam(0, v->index);
01064 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01065 }
01066 } else if (v->owner == _local_company && cost.GetCost() != 0) {
01067 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01068 }
01069 }
01070
01071 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01072
01073 UpdateVehicleTimetable(v, true);
01074 v->IncrementOrderIndex();
01075 }
01076 if (t.GetDepotActionType() & ODATFB_HALT) {
01077
01078 _vehicles_to_autoreplace[v] = false;
01079 if (v->owner == _local_company) {
01080 SetDParam(0, v->index);
01081 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01082 }
01083 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01084 }
01085 }
01086 }
01087
01088
01096 void VehicleMove(Vehicle *v, bool update_viewport)
01097 {
01098 int img = v->cur_image;
01099 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01100 const Sprite *spr = GetSprite(img, ST_NORMAL);
01101
01102 pt.x += spr->x_offs;
01103 pt.y += spr->y_offs;
01104
01105 UpdateVehiclePosHash(v, pt.x, pt.y);
01106
01107 Rect old_coord = v->coord;
01108 v->coord.left = pt.x;
01109 v->coord.top = pt.y;
01110 v->coord.right = pt.x + spr->width + 2;
01111 v->coord.bottom = pt.y + spr->height + 2;
01112
01113 if (update_viewport) {
01114 MarkAllViewportsDirty(
01115 min(old_coord.left, v->coord.left),
01116 min(old_coord.top, v->coord.top),
01117 max(old_coord.right, v->coord.right) + 1,
01118 max(old_coord.bottom, v->coord.bottom) + 1
01119 );
01120 }
01121 }
01122
01131 void MarkSingleVehicleDirty(const Vehicle *v)
01132 {
01133 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01134 }
01135
01140 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01141 {
01142 static const int8 _delta_coord[16] = {
01143 -1,-1,-1, 0, 1, 1, 1, 0,
01144 -1, 0, 1, 1, 1, 0,-1,-1,
01145 };
01146
01147 int x = v->x_pos + _delta_coord[v->direction];
01148 int y = v->y_pos + _delta_coord[v->direction + 8];
01149
01150 GetNewVehiclePosResult gp;
01151 gp.x = x;
01152 gp.y = y;
01153 gp.old_tile = v->tile;
01154 gp.new_tile = TileVirtXY(x, y);
01155 return gp;
01156 }
01157
01158 static const Direction _new_direction_table[] = {
01159 DIR_N, DIR_NW, DIR_W,
01160 DIR_NE, DIR_SE, DIR_SW,
01161 DIR_E, DIR_SE, DIR_S
01162 };
01163
01164 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01165 {
01166 int i = 0;
01167
01168 if (y >= v->y_pos) {
01169 if (y != v->y_pos) i += 3;
01170 i += 3;
01171 }
01172
01173 if (x >= v->x_pos) {
01174 if (x != v->x_pos) i++;
01175 i++;
01176 }
01177
01178 Direction dir = v->direction;
01179
01180 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01181 if (dirdiff == DIRDIFF_SAME) return dir;
01182 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01183 }
01184
01194 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01195 {
01196 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01197 }
01198
01199 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01200 {
01201
01202 const Vehicle *v;
01203 FOR_ALL_VEHICLES(v) {
01204 if (v->type == type && v->owner == owner) {
01205 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01206 }
01207 }
01208
01209 if (this->maxid == 0) return;
01210
01211 this->maxid++;
01212 this->maxid++;
01213
01214 this->cache = CallocT<bool>(this->maxid);
01215
01216
01217 FOR_ALL_VEHICLES(v) {
01218 if (v->type == type && v->owner == owner) {
01219 this->cache[v->unitnumber] = true;
01220 }
01221 }
01222 }
01223
01224 UnitID FreeUnitIDGenerator::NextID()
01225 {
01226 if (this->maxid <= this->curid) return ++this->curid;
01227
01228 while (this->cache[++this->curid]) { }
01229
01230 return this->curid;
01231 }
01232
01233 UnitID GetFreeUnitNumber(VehicleType type)
01234 {
01235 FreeUnitIDGenerator gen(type, _current_company);
01236
01237 return gen.NextID();
01238 }
01239
01240
01249 bool CanBuildVehicleInfrastructure(VehicleType type)
01250 {
01251 assert(IsCompanyBuildableVehicleType(type));
01252
01253 if (!Company::IsValidID(_local_company)) return false;
01254 if (_settings_client.gui.always_build_infrastructure) return true;
01255
01256 UnitID max;
01257 switch (type) {
01258 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01259 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01260 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01261 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01262 default: NOT_REACHED();
01263 }
01264
01265
01266 if (max > 0) {
01267
01268 const Engine *e;
01269 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01270 if (HasBit(e->company_avail, _local_company)) return true;
01271 }
01272 return false;
01273 }
01274
01275
01276 const Vehicle *v;
01277 FOR_ALL_VEHICLES(v) {
01278 if (v->owner == _local_company && v->type == type) return true;
01279 }
01280
01281 return false;
01282 }
01283
01284
01293 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01294 {
01295 const Company *c = Company::Get(company);
01296 LiveryScheme scheme = LS_DEFAULT;
01297 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01298
01299
01300
01301 if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
01302
01303 const Engine *e = Engine::Get(engine_type);
01304 switch (e->type) {
01305 default: NOT_REACHED();
01306 case VEH_TRAIN: {
01307 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (Train::From(v)->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01308
01309
01310 engine_type = parent_engine_type;
01311 e = Engine::Get(engine_type);
01312
01313 }
01314
01315 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01316 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01317 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01318 if (!CargoSpec::Get(cargo_type)->is_freight) {
01319 if (parent_engine_type == INVALID_ENGINE) {
01320 scheme = LS_PASSENGER_WAGON_STEAM;
01321 } else {
01322 switch (RailVehInfo(parent_engine_type)->engclass) {
01323 default: NOT_REACHED();
01324 case EC_STEAM: scheme = LS_PASSENGER_WAGON_STEAM; break;
01325 case EC_DIESEL: scheme = LS_PASSENGER_WAGON_DIESEL; break;
01326 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
01327 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
01328 case EC_MAGLEV: scheme = LS_PASSENGER_WAGON_MAGLEV; break;
01329 }
01330 }
01331 } else {
01332 scheme = LS_FREIGHT_WAGON;
01333 }
01334 } else {
01335 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01336
01337 switch (e->u.rail.engclass) {
01338 default: NOT_REACHED();
01339 case EC_STEAM: scheme = LS_STEAM; break;
01340 case EC_DIESEL: scheme = is_mu ? LS_DMU : LS_DIESEL; break;
01341 case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
01342 case EC_MONORAIL: scheme = LS_MONORAIL; break;
01343 case EC_MAGLEV: scheme = LS_MAGLEV; break;
01344 }
01345 }
01346 break;
01347 }
01348
01349 case VEH_ROAD: {
01350
01351 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01352 engine_type = parent_engine_type;
01353 e = Engine::Get(engine_type);
01354 cargo_type = v->First()->cargo_type;
01355 }
01356 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01357 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01358
01359
01360 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01361
01362 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01363 } else {
01364
01365 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01366 }
01367 break;
01368 }
01369
01370 case VEH_SHIP: {
01371 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01372 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01373 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01374 break;
01375 }
01376
01377 case VEH_AIRCRAFT: {
01378 switch (e->u.air.subtype) {
01379 case AIR_HELI: scheme = LS_HELICOPTER; break;
01380 case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
01381 case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
01382 }
01383 break;
01384 }
01385 }
01386
01387
01388 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01389 }
01390
01391 return &c->livery[scheme];
01392 }
01393
01394
01395 static SpriteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01396 {
01397 SpriteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01398
01399
01400 if (map != PAL_NONE) return map;
01401
01402 const Engine *e = Engine::Get(engine_type);
01403
01404
01405 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01406 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01407
01408
01409 if (callback != CALLBACK_FAILED && callback != 0xC000) {
01410 map = GB(callback, 0, 14);
01411
01412
01413 if (!HasBit(callback, 14)) {
01414
01415 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01416 return map;
01417 }
01418 }
01419 }
01420
01421 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01422
01423 if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOUR_START;
01424
01425
01426 if (!Company::IsValidID(company)) return map;
01427
01428 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
01429
01430 map += livery->colour1;
01431 if (twocc) map += livery->colour2 * 16;
01432
01433
01434 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01435 return map;
01436 }
01437
01438 SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
01439 {
01440 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01441 }
01442
01443 SpriteID GetVehiclePalette(const Vehicle *v)
01444 {
01445 if (v->type == VEH_TRAIN) {
01446 return GetEngineColourMap(v->engine_type, v->owner, Train::From(v)->tcache.first_engine, v);
01447 } else if (v->type == VEH_ROAD) {
01448 return GetEngineColourMap(v->engine_type, v->owner, RoadVehicle::From(v)->rcache.first_engine, v);
01449 }
01450
01451 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01452 }
01453
01462 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01463 {
01464 if (mail_capacity != NULL) *mail_capacity = 0;
01465 const Engine *e = Engine::Get(v->engine_type);
01466
01467 if (!e->CanCarryCargo()) return 0;
01468
01469 if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01470 *mail_capacity = e->u.air.mail_capacity;
01471 }
01472 CargoID default_cargo = e->GetDefaultCargoType();
01473
01474
01475
01476 if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01477 (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01478 uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01479 if (callback != CALLBACK_FAILED) return callback;
01480 }
01481
01482
01483 uint capacity;
01484 switch (e->type) {
01485 case VEH_TRAIN: capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY, e->u.rail.capacity); break;
01486 case VEH_ROAD: capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
01487 case VEH_SHIP: capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY, e->u.ship.capacity); break;
01488 case VEH_AIRCRAFT: capacity = e->u.air.passenger_capacity; break;
01489 default: NOT_REACHED();
01490 }
01491
01492
01493
01494 if (e->type != VEH_SHIP) {
01495 if (e->type == VEH_AIRCRAFT) {
01496 if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01497 capacity += e->u.air.mail_capacity;
01498 }
01499 if (v->cargo_type == CT_MAIL) return capacity;
01500 } else {
01501 switch (default_cargo) {
01502 case CT_PASSENGERS: break;
01503 case CT_MAIL:
01504 case CT_GOODS: capacity *= 2; break;
01505 default: capacity *= 4; break;
01506 }
01507 }
01508 switch (v->cargo_type) {
01509 case CT_PASSENGERS: break;
01510 case CT_MAIL:
01511 case CT_GOODS: capacity /= 2; break;
01512 default: capacity /= 4; break;
01513 }
01514 }
01515
01516 return capacity;
01517 }
01518
01519
01520 void Vehicle::BeginLoading()
01521 {
01522 assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01523
01524 if (this->current_order.IsType(OT_GOTO_STATION) &&
01525 this->current_order.GetDestination() == this->last_station_visited) {
01526 current_order.MakeLoading(true);
01527 UpdateVehicleTimetable(this, true);
01528
01529
01530
01531
01532
01533
01534 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01535
01536 } else {
01537 current_order.MakeLoading(false);
01538 }
01539
01540 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01541
01542 PrepareUnload(this);
01543
01544 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01545 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01546 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01547 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01548
01549 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01550 this->cur_speed = 0;
01551 this->MarkDirty();
01552 }
01553
01554 void Vehicle::LeaveStation()
01555 {
01556 assert(current_order.IsType(OT_LOADING));
01557
01558 delete this->cargo_payment;
01559
01560
01561 if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01562
01563 current_order.MakeLeaveStation();
01564 Station *st = Station::Get(this->last_station_visited);
01565 st->loading_vehicles.remove(this);
01566
01567 HideFillingPercent(&this->fill_percent_te_id);
01568
01569 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01570
01571 if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
01572
01573
01574
01575
01576 if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(this->GetVehicleTrackdir()), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
01577 TryPathReserve(Train::From(this), true, true);
01578 }
01579 }
01580 }
01581
01582
01583 void Vehicle::HandleLoading(bool mode)
01584 {
01585 switch (this->current_order.GetType()) {
01586 case OT_LOADING: {
01587 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01588
01589
01590 if (!mode && this->type != VEH_TRAIN) PayStationSharingFee(this, Station::Get(this->last_station_visited));
01591
01592
01593 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01594 (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01595
01596 this->PlayLeaveStationSound();
01597
01598 bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
01599 this->LeaveStation();
01600
01601
01602 if (!at_destination_station) return;
01603 break;
01604 }
01605
01606 case OT_DUMMY: break;
01607
01608 default: return;
01609 }
01610
01611 this->IncrementOrderIndex();
01612 }
01613
01614 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01615 {
01616 if (!CheckOwnership(this->owner)) return CMD_ERROR;
01617 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01618 if (this->IsStoppedInDepot()) return CMD_ERROR;
01619
01620 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01621 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
01622 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01623
01624
01625
01626 if (flags & DC_EXEC) {
01627 this->current_order.SetDepotOrderType(ODTF_MANUAL);
01628 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01629 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01630 }
01631 return CommandCost();
01632 }
01633
01634 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
01635 if (flags & DC_EXEC) {
01636
01637
01638 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementOrderIndex();
01639
01640 this->current_order.MakeDummy();
01641 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01642 }
01643 return CommandCost();
01644 }
01645
01646 TileIndex location;
01647 DestinationID destination;
01648 bool reverse;
01649 static const StringID no_depot[] = {STR_ERROR_UNABLE_TO_FIND_ROUTE_TO, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_CAN_T_SEND_AIRCRAFT_TO_HANGAR};
01650 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01651
01652 if (flags & DC_EXEC) {
01653 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01654
01655 this->dest_tile = location;
01656 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01657 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01658 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01659
01660
01661 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01662
01663 if (this->type == VEH_AIRCRAFT) {
01664 Aircraft *a = Aircraft::From(this);
01665 if (a->state == FLYING && a->targetairport != destination) {
01666
01667 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01668 AircraftNextAirportPos_and_Order(a);
01669 }
01670 }
01671 }
01672
01673 return CommandCost();
01674
01675 }
01676
01677 void Vehicle::SetNext(Vehicle *next)
01678 {
01679 assert(this != next);
01680
01681 if (this->next != NULL) {
01682
01683 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01684 v->first = this->next;
01685 }
01686 this->next->previous = NULL;
01687 }
01688
01689 this->next = next;
01690
01691 if (this->next != NULL) {
01692
01693 if (this->next->previous != NULL) this->next->previous->next = NULL;
01694 this->next->previous = this;
01695 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01696 v->first = this->first;
01697 }
01698 }
01699 }
01700
01701 void Vehicle::AddToShared(Vehicle *shared_chain)
01702 {
01703 assert(this->previous_shared == NULL && this->next_shared == NULL);
01704
01705 if (!shared_chain->orders.list) {
01706 assert(shared_chain->previous_shared == NULL);
01707 assert(shared_chain->next_shared == NULL);
01708 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
01709 }
01710
01711 this->next_shared = shared_chain->next_shared;
01712 this->previous_shared = shared_chain;
01713
01714 shared_chain->next_shared = this;
01715
01716 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
01717
01718 shared_chain->orders.list->AddVehicle(this);
01719 }
01720
01721 void Vehicle::RemoveFromShared()
01722 {
01723
01724
01725 bool were_first = (this->FirstShared() == this);
01726 uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
01727
01728 this->orders.list->RemoveVehicle(this);
01729
01730 if (!were_first) {
01731
01732 this->previous_shared->next_shared = this->NextShared();
01733 }
01734
01735 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
01736
01737
01738 if (this->orders.list->GetNumVehicles() == 1) {
01739
01740 DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
01741 InvalidateVehicleOrder(this->FirstShared(), 0);
01742 } else if (were_first) {
01743
01744
01745 InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
01746 }
01747
01748 this->next_shared = NULL;
01749 this->previous_shared = NULL;
01750 }
01751
01752 void StopAllVehicles()
01753 {
01754 Vehicle *v;
01755 FOR_ALL_VEHICLES(v) {
01756
01757
01758 v->vehstatus |= VS_STOPPED;
01759 v->MarkDirty();
01760 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01761 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01762 }
01763 }
01764
01765 void VehiclesYearlyLoop()
01766 {
01767 Vehicle *v;
01768 FOR_ALL_VEHICLES(v) {
01769 if (v->IsPrimaryVehicle()) {
01770
01771 Money profit = v->GetDisplayProfitThisYear();
01772 if (v->age >= 730 && profit < 0) {
01773 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
01774 SetDParam(0, v->index);
01775 SetDParam(1, profit);
01776 AddVehicleNewsItem(
01777 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
01778 NS_ADVICE,
01779 v->index
01780 );
01781 }
01782 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
01783 }
01784
01785 v->profit_last_year = v->profit_this_year;
01786 v->profit_this_year = 0;
01787 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01788 }
01789 }
01790 }
01791
01792
01802 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
01803 {
01804 const Engine *e = Engine::GetIfValid(engine_type);
01805 assert(e != NULL);
01806
01807 switch (e->type) {
01808 case VEH_TRAIN:
01809 return (st->facilities & FACIL_TRAIN) != 0;
01810
01811 case VEH_ROAD:
01812
01813
01814
01815 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
01816
01817 case VEH_SHIP:
01818 return (st->facilities & FACIL_DOCK) != 0;
01819
01820 case VEH_AIRCRAFT:
01821 return (st->facilities & FACIL_AIRPORT) != 0 &&
01822 (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
01823
01824 default:
01825 return false;
01826 }
01827 }
01828
01835 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
01836 {
01837 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
01838
01839 return CanVehicleUseStation(v->engine_type, st);
01840 }