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