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