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