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 "infrastructure_func.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 if (this->Previous() == NULL) {
00579 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00580 }
00581
00582 if (this->IsPrimaryVehicle()) {
00583 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00584 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00585 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00586 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00587 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00588 SetWindowDirty(WC_COMPANY, this->owner);
00589 }
00590 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00591
00592 this->cargo.Truncate(0);
00593 DeleteVehicleOrders(this);
00594 DeleteDepotHighlightOfVehicle(this);
00595
00596 extern void StopGlobalFollowVehicle(const Vehicle *v);
00597 StopGlobalFollowVehicle(this);
00598
00599 ReleaseDisastersTargetingVehicle(this->index);
00600 }
00601
00602 Vehicle::~Vehicle()
00603 {
00604 free(this->name);
00605
00606 if (CleaningPool()) return;
00607
00608
00609
00610 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00611
00612 Vehicle *v = this->Next();
00613 this->SetNext(NULL);
00614
00615 delete v;
00616
00617 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00618 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00619 }
00620
00624 void VehicleEnteredDepotThisTick(Vehicle *v)
00625 {
00626
00627 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00628
00629
00630
00631
00632
00633
00634 v->vehstatus |= VS_STOPPED;
00635 }
00636
00642 static void RunVehicleDayProc()
00643 {
00644 if (_game_mode != GM_NORMAL) return;
00645
00646
00647 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00648 Vehicle *v = Vehicle::Get(i);
00649 if (v == NULL) continue;
00650
00651
00652 if ((v->day_counter & 0x1F) == 0) {
00653 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00654 if (callback != CALLBACK_FAILED) {
00655 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00656 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00657 }
00658 }
00659
00660
00661 v->OnNewDay();
00662 }
00663 }
00664
00665 void CallVehicleTicks()
00666 {
00667 _vehicles_to_autoreplace.Clear();
00668
00669 _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
00670
00671 RunVehicleDayProc();
00672
00673 Station *st;
00674 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00675
00676 Vehicle *v;
00677 FOR_ALL_VEHICLES(v) {
00678
00679 if (!v->Tick()) {
00680 assert(Vehicle::Get(vehicle_index) == NULL);
00681 continue;
00682 }
00683
00684 assert(Vehicle::Get(vehicle_index) == v);
00685
00686 switch (v->type) {
00687 default: break;
00688
00689 case VEH_TRAIN:
00690 case VEH_ROAD:
00691 case VEH_AIRCRAFT:
00692 case VEH_SHIP:
00693 if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
00694
00695 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00696 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00697 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsRoadVehFront()) continue;
00698
00699 v->motion_counter += v->cur_speed;
00700
00701 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00702
00703
00704 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00705 }
00706 }
00707
00708 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00709 v = it->first;
00710
00711 _current_company = v->owner;
00712
00713
00714
00715
00716 if (it->second) v->vehstatus &= ~VS_STOPPED;
00717
00718
00719 int x = v->x_pos;
00720 int y = v->y_pos;
00721 int z = v->z_pos;
00722
00723 const Company *c = Company::Get(_current_company);
00724 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00725 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00726 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00727
00728 if (!IsLocalCompany()) continue;
00729
00730 if (res.Succeeded()) {
00731 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00732 continue;
00733 }
00734
00735 StringID error_message = res.GetErrorMessage();
00736 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00737
00738 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00739
00740 StringID message;
00741 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00742 message = error_message;
00743 } else {
00744 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00745 }
00746
00747 SetDParam(0, v->index);
00748 SetDParam(1, error_message);
00749 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00750 }
00751
00752 _current_company = OWNER_NONE;
00753 }
00754
00755 static void DoDrawVehicle(const Vehicle *v)
00756 {
00757 SpriteID image = v->cur_image;
00758 SpriteID pal = PAL_NONE;
00759
00760 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00761
00762 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00763 v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00764 }
00765
00766 void ViewportAddVehicles(DrawPixelInfo *dpi)
00767 {
00768
00769 const int l = dpi->left;
00770 const int r = dpi->left + dpi->width;
00771 const int t = dpi->top;
00772 const int b = dpi->top + dpi->height;
00773
00774
00775 int xl, xu, yl, yu;
00776
00777 if (dpi->width + 70 < (1 << (7 + 6))) {
00778 xl = GB(l - 70, 7, 6);
00779 xu = GB(r, 7, 6);
00780 } else {
00781
00782 xl = 0;
00783 xu = 0x3F;
00784 }
00785
00786 if (dpi->height + 70 < (1 << (6 + 6))) {
00787 yl = GB(t - 70, 6, 6) << 6;
00788 yu = GB(b, 6, 6) << 6;
00789 } else {
00790
00791 yl = 0;
00792 yu = 0x3F << 6;
00793 }
00794
00795 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00796 for (int x = xl;; x = (x + 1) & 0x3F) {
00797 const Vehicle *v = _vehicle_position_hash[x + y];
00798
00799 while (v != NULL) {
00800 if (!(v->vehstatus & VS_HIDDEN) &&
00801 l <= v->coord.right &&
00802 t <= v->coord.bottom &&
00803 r >= v->coord.left &&
00804 b >= v->coord.top) {
00805 DoDrawVehicle(v);
00806 }
00807 v = v->next_hash;
00808 }
00809
00810 if (x == xu) break;
00811 }
00812
00813 if (y == yu) break;
00814 }
00815 }
00816
00817 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00818 {
00819 Vehicle *found = NULL, *v;
00820 uint dist, best_dist = UINT_MAX;
00821
00822 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00823
00824 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00825 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00826
00827 FOR_ALL_VEHICLES(v) {
00828 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00829 x >= v->coord.left && x <= v->coord.right &&
00830 y >= v->coord.top && y <= v->coord.bottom) {
00831
00832 dist = max(
00833 abs(((v->coord.left + v->coord.right) >> 1) - x),
00834 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00835 );
00836
00837 if (dist < best_dist) {
00838 found = v;
00839 best_dist = dist;
00840 }
00841 }
00842 }
00843
00844 return found;
00845 }
00846
00847 void DecreaseVehicleValue(Vehicle *v)
00848 {
00849 v->value -= v->value >> 8;
00850 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00851 }
00852
00853 static const byte _breakdown_chance[64] = {
00854 3, 3, 3, 3, 3, 3, 3, 3,
00855 4, 4, 5, 5, 6, 6, 7, 7,
00856 8, 8, 9, 9, 10, 10, 11, 11,
00857 12, 13, 13, 13, 13, 14, 15, 16,
00858 17, 19, 21, 25, 28, 31, 34, 37,
00859 40, 44, 48, 52, 56, 60, 64, 68,
00860 72, 80, 90, 100, 110, 120, 130, 140,
00861 150, 170, 190, 210, 230, 250, 250, 250,
00862 };
00863
00864 void CheckVehicleBreakdown(Vehicle *v)
00865 {
00866 int rel, rel_old;
00867
00868
00869 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
00870 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00871
00872 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
00873 _settings_game.difficulty.vehicle_breakdowns < 1 ||
00874 v->cur_speed < 5 || _game_mode == GM_MENU) {
00875 return;
00876 }
00877
00878 uint32 r = Random();
00879
00880
00881 int chance = v->breakdown_chance + 1;
00882 if (Chance16I(1, 25, r)) chance += 25;
00883 v->breakdown_chance = min(255, chance);
00884
00885
00886 rel = v->reliability;
00887 if (v->type == VEH_SHIP) rel += 0x6666;
00888
00889
00890 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
00891
00892
00893 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
00894 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
00895 v->breakdown_delay = GB(r, 24, 7) + 0x80;
00896 v->breakdown_chance = 0;
00897 }
00898 }
00899
00900 void AgeVehicle(Vehicle *v)
00901 {
00902 if (v->age < 65535) v->age++;
00903
00904 int age = v->age - v->max_age;
00905 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
00906 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
00907 v->reliability_spd_dec <<= 1;
00908 }
00909
00910 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00911
00912
00913 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
00914
00915
00916 if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
00917
00918 StringID str;
00919 if (age == -DAYS_IN_LEAP_YEAR) {
00920 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
00921 } else if (age == 0) {
00922 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
00923 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
00924 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
00925 } else {
00926 return;
00927 }
00928
00929 SetDParam(0, v->index);
00930 AddVehicleNewsItem(str, NS_ADVICE, v->index);
00931 }
00932
00939 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
00940 {
00941 int count = 0;
00942 int max = 0;
00943 int cars = 0;
00944 int unloading = 0;
00945 bool loading = false;
00946
00947 const Vehicle *u = v;
00948 const Station *st = v->last_station_visited != INVALID_STATION ? Station::Get(v->last_station_visited) : NULL;
00949
00950
00951 for (; v != NULL; v = v->Next()) {
00952 count += v->cargo.Count();
00953 max += v->cargo_cap;
00954 if (v->cargo_cap != 0 && colour != NULL) {
00955 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
00956 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
00957 cars++;
00958 }
00959 }
00960
00961 if (colour != NULL) {
00962 if (unloading == 0 && loading) {
00963 *colour = STR_PERCENT_UP;
00964 } else if (cars == unloading || !loading) {
00965 *colour = STR_PERCENT_DOWN;
00966 } else {
00967 *colour = STR_PERCENT_UP_DOWN;
00968 }
00969 }
00970
00971
00972 if (max == 0) return 100;
00973
00974
00975 return (count * 100) / max;
00976 }
00977
00978 void VehicleEnterDepot(Vehicle *v)
00979 {
00980
00981 assert(v == v->First());
00982
00983 switch (v->type) {
00984 case VEH_TRAIN: {
00985 Train *t = Train::From(v);
00986 SetWindowClassesDirty(WC_TRAINS_LIST);
00987
00988 SetDepotReservation(t->tile, false);
00989 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
00990
00991 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
00992 t->time_counter = 0;
00993 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
00994 TrainConsistChanged(t, true);
00995 break;
00996 }
00997
00998 case VEH_ROAD:
00999 SetWindowClassesDirty(WC_ROADVEH_LIST);
01000 break;
01001
01002 case VEH_SHIP:
01003 SetWindowClassesDirty(WC_SHIPS_LIST);
01004 Ship::From(v)->state = TRACK_BIT_DEPOT;
01005 RecalcShipStuff(v);
01006 break;
01007
01008 case VEH_AIRCRAFT:
01009 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01010 HandleAircraftEnterHangar(Aircraft::From(v));
01011 break;
01012 default: NOT_REACHED();
01013 }
01014
01015 if (v->type != VEH_TRAIN) {
01016
01017
01018 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01019 }
01020 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01021
01022 v->vehstatus |= VS_HIDDEN;
01023 v->cur_speed = 0;
01024
01025 VehicleServiceInDepot(v);
01026
01027 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01028
01029 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01030 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01031
01032 const Order *real_order = v->GetOrder(v->cur_order_index);
01033 Order t = v->current_order;
01034 v->current_order.MakeDummy();
01035
01036
01037
01038 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01039 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01040 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01041
01042 return;
01043 }
01044
01045 if (t.IsRefit()) {
01046 _current_company = v->owner;
01047 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01048
01049 if (CmdFailed(cost)) {
01050 _vehicles_to_autoreplace[v] = false;
01051 if (v->owner == _local_company) {
01052
01053 SetDParam(0, v->index);
01054 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01055 }
01056 } else if (v->owner == _local_company && cost.GetCost() != 0) {
01057 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01058 }
01059 }
01060
01061 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01062
01063 UpdateVehicleTimetable(v, true);
01064 v->IncrementOrderIndex();
01065 }
01066 if (t.GetDepotActionType() & ODATFB_HALT) {
01067
01068 _vehicles_to_autoreplace[v] = false;
01069 if (v->owner == _local_company) {
01070 SetDParam(0, v->index);
01071 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01072 }
01073 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01074 }
01075 }
01076 }
01077
01078
01086 void VehicleMove(Vehicle *v, bool update_viewport)
01087 {
01088 int img = v->cur_image;
01089 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01090 const Sprite *spr = GetSprite(img, ST_NORMAL);
01091
01092 pt.x += spr->x_offs;
01093 pt.y += spr->y_offs;
01094
01095 UpdateVehiclePosHash(v, pt.x, pt.y);
01096
01097 Rect old_coord = v->coord;
01098 v->coord.left = pt.x;
01099 v->coord.top = pt.y;
01100 v->coord.right = pt.x + spr->width + 2;
01101 v->coord.bottom = pt.y + spr->height + 2;
01102
01103 if (update_viewport) {
01104 MarkAllViewportsDirty(
01105 min(old_coord.left, v->coord.left),
01106 min(old_coord.top, v->coord.top),
01107 max(old_coord.right, v->coord.right) + 1,
01108 max(old_coord.bottom, v->coord.bottom) + 1
01109 );
01110 }
01111 }
01112
01121 void MarkSingleVehicleDirty(const Vehicle *v)
01122 {
01123 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01124 }
01125
01130 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01131 {
01132 static const int8 _delta_coord[16] = {
01133 -1,-1,-1, 0, 1, 1, 1, 0,
01134 -1, 0, 1, 1, 1, 0,-1,-1,
01135 };
01136
01137 int x = v->x_pos + _delta_coord[v->direction];
01138 int y = v->y_pos + _delta_coord[v->direction + 8];
01139
01140 GetNewVehiclePosResult gp;
01141 gp.x = x;
01142 gp.y = y;
01143 gp.old_tile = v->tile;
01144 gp.new_tile = TileVirtXY(x, y);
01145 return gp;
01146 }
01147
01148 static const Direction _new_direction_table[] = {
01149 DIR_N, DIR_NW, DIR_W,
01150 DIR_NE, DIR_SE, DIR_SW,
01151 DIR_E, DIR_SE, DIR_S
01152 };
01153
01154 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01155 {
01156 int i = 0;
01157
01158 if (y >= v->y_pos) {
01159 if (y != v->y_pos) i += 3;
01160 i += 3;
01161 }
01162
01163 if (x >= v->x_pos) {
01164 if (x != v->x_pos) i++;
01165 i++;
01166 }
01167
01168 Direction dir = v->direction;
01169
01170 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01171 if (dirdiff == DIRDIFF_SAME) return dir;
01172 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01173 }
01174
01184 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01185 {
01186 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01187 }
01188
01189 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01190 {
01191
01192 const Vehicle *v;
01193 FOR_ALL_VEHICLES(v) {
01194 if (v->type == type && v->owner == owner) {
01195 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01196 }
01197 }
01198
01199 if (this->maxid == 0) return;
01200
01201 this->maxid++;
01202 this->maxid++;
01203
01204 this->cache = CallocT<bool>(this->maxid);
01205
01206
01207 FOR_ALL_VEHICLES(v) {
01208 if (v->type == type && v->owner == owner) {
01209 this->cache[v->unitnumber] = true;
01210 }
01211 }
01212 }
01213
01214 UnitID FreeUnitIDGenerator::NextID()
01215 {
01216 if (this->maxid <= this->curid) return ++this->curid;
01217
01218 while (this->cache[++this->curid]) { }
01219
01220 return this->curid;
01221 }
01222
01223 UnitID GetFreeUnitNumber(VehicleType type)
01224 {
01225 FreeUnitIDGenerator gen(type, _current_company);
01226
01227 return gen.NextID();
01228 }
01229
01230
01239 bool CanBuildVehicleInfrastructure(VehicleType type)
01240 {
01241 assert(IsCompanyBuildableVehicleType(type));
01242
01243 if (!Company::IsValidID(_local_company)) return false;
01244 if (_settings_client.gui.always_build_infrastructure) return true;
01245
01246 UnitID max;
01247 switch (type) {
01248 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01249 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01250 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01251 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01252 default: NOT_REACHED();
01253 }
01254
01255
01256 if (max > 0) {
01257
01258 const Engine *e;
01259 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01260 if (HasBit(e->company_avail, _local_company)) return true;
01261 }
01262 return false;
01263 }
01264
01265
01266 const Vehicle *v;
01267 FOR_ALL_VEHICLES(v) {
01268 if (v->owner == _local_company && v->type == type) return true;
01269 }
01270
01271 return false;
01272 }
01273
01274
01283 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01284 {
01285 const Company *c = Company::Get(company);
01286 LiveryScheme scheme = LS_DEFAULT;
01287 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01288
01289
01290
01291 if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
01292
01293 const Engine *e = Engine::Get(engine_type);
01294 switch (e->type) {
01295 default: NOT_REACHED();
01296 case VEH_TRAIN: {
01297 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (Train::From(v)->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01298
01299
01300 engine_type = parent_engine_type;
01301 e = Engine::Get(engine_type);
01302
01303 }
01304
01305 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01306 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01307 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01308 if (!CargoSpec::Get(cargo_type)->is_freight) {
01309 if (parent_engine_type == INVALID_ENGINE) {
01310 scheme = LS_PASSENGER_WAGON_STEAM;
01311 } else {
01312 switch (RailVehInfo(parent_engine_type)->engclass) {
01313 default: NOT_REACHED();
01314 case EC_STEAM: scheme = LS_PASSENGER_WAGON_STEAM; break;
01315 case EC_DIESEL: scheme = LS_PASSENGER_WAGON_DIESEL; break;
01316 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
01317 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
01318 case EC_MAGLEV: scheme = LS_PASSENGER_WAGON_MAGLEV; break;
01319 }
01320 }
01321 } else {
01322 scheme = LS_FREIGHT_WAGON;
01323 }
01324 } else {
01325 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01326
01327 switch (e->u.rail.engclass) {
01328 default: NOT_REACHED();
01329 case EC_STEAM: scheme = LS_STEAM; break;
01330 case EC_DIESEL: scheme = is_mu ? LS_DMU : LS_DIESEL; break;
01331 case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
01332 case EC_MONORAIL: scheme = LS_MONORAIL; break;
01333 case EC_MAGLEV: scheme = LS_MAGLEV; break;
01334 }
01335 }
01336 break;
01337 }
01338
01339 case VEH_ROAD: {
01340
01341 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01342 engine_type = parent_engine_type;
01343 e = Engine::Get(engine_type);
01344 cargo_type = v->First()->cargo_type;
01345 }
01346 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01347 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01348
01349
01350 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01351
01352 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01353 } else {
01354
01355 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01356 }
01357 break;
01358 }
01359
01360 case VEH_SHIP: {
01361 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01362 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01363 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01364 break;
01365 }
01366
01367 case VEH_AIRCRAFT: {
01368 switch (e->u.air.subtype) {
01369 case AIR_HELI: scheme = LS_HELICOPTER; break;
01370 case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
01371 case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
01372 }
01373 break;
01374 }
01375 }
01376
01377
01378 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01379 }
01380
01381 return &c->livery[scheme];
01382 }
01383
01384
01385 static SpriteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01386 {
01387 SpriteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01388
01389
01390 if (map != PAL_NONE) return map;
01391
01392 const Engine *e = Engine::Get(engine_type);
01393
01394
01395 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01396 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01397
01398
01399 if (callback != CALLBACK_FAILED && callback != 0xC000) {
01400 map = GB(callback, 0, 14);
01401
01402
01403 if (!HasBit(callback, 14)) {
01404
01405 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01406 return map;
01407 }
01408 }
01409 }
01410
01411 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01412
01413 if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOUR_START;
01414
01415
01416 if (!Company::IsValidID(company)) return map;
01417
01418 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
01419
01420 map += livery->colour1;
01421 if (twocc) map += livery->colour2 * 16;
01422
01423
01424 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01425 return map;
01426 }
01427
01428 SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
01429 {
01430 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01431 }
01432
01433 SpriteID GetVehiclePalette(const Vehicle *v)
01434 {
01435 if (v->type == VEH_TRAIN) {
01436 return GetEngineColourMap(v->engine_type, v->owner, Train::From(v)->tcache.first_engine, v);
01437 } else if (v->type == VEH_ROAD) {
01438 return GetEngineColourMap(v->engine_type, v->owner, RoadVehicle::From(v)->rcache.first_engine, v);
01439 }
01440
01441 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01442 }
01443
01452 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01453 {
01454 if (mail_capacity != NULL) *mail_capacity = 0;
01455 const Engine *e = Engine::Get(v->engine_type);
01456
01457 if (!e->CanCarryCargo()) return 0;
01458
01459 if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01460 *mail_capacity = e->u.air.mail_capacity;
01461 }
01462 CargoID default_cargo = e->GetDefaultCargoType();
01463
01464
01465
01466 if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01467 (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01468 uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01469 if (callback != CALLBACK_FAILED) return callback;
01470 }
01471
01472
01473 uint capacity;
01474 switch (e->type) {
01475 case VEH_TRAIN: capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY, e->u.rail.capacity); break;
01476 case VEH_ROAD: capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
01477 case VEH_SHIP: capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY, e->u.ship.capacity); break;
01478 case VEH_AIRCRAFT: capacity = e->u.air.passenger_capacity; break;
01479 default: NOT_REACHED();
01480 }
01481
01482
01483
01484 if (e->type != VEH_SHIP) {
01485 if (e->type == VEH_AIRCRAFT) {
01486 if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01487 capacity += e->u.air.mail_capacity;
01488 }
01489 if (v->cargo_type == CT_MAIL) return capacity;
01490 } else {
01491 switch (default_cargo) {
01492 case CT_PASSENGERS: break;
01493 case CT_MAIL:
01494 case CT_GOODS: capacity *= 2; break;
01495 default: capacity *= 4; break;
01496 }
01497 }
01498 switch (v->cargo_type) {
01499 case CT_PASSENGERS: break;
01500 case CT_MAIL:
01501 case CT_GOODS: capacity /= 2; break;
01502 default: capacity /= 4; break;
01503 }
01504 }
01505
01506 return capacity;
01507 }
01508
01509
01510 void Vehicle::BeginLoading()
01511 {
01512 assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01513
01514 if (this->current_order.IsType(OT_GOTO_STATION) &&
01515 this->current_order.GetDestination() == this->last_station_visited) {
01516 current_order.MakeLoading(true);
01517 UpdateVehicleTimetable(this, true);
01518
01519
01520
01521
01522
01523
01524 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01525
01526 } else {
01527 current_order.MakeLoading(false);
01528 }
01529
01530 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01531
01532 PrepareUnload(this);
01533
01534 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01535 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01536 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01537 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01538
01539 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01540 this->cur_speed = 0;
01541 this->MarkDirty();
01542 }
01543
01544 void Vehicle::LeaveStation()
01545 {
01546 assert(current_order.IsType(OT_LOADING));
01547
01548 delete this->cargo_payment;
01549
01550
01551 if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01552
01553 current_order.MakeLeaveStation();
01554 Station *st = Station::Get(this->last_station_visited);
01555 st->loading_vehicles.remove(this);
01556
01557 HideFillingPercent(&this->fill_percent_te_id);
01558
01559 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01560
01561 if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
01562
01563
01564
01565
01566 if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(this->GetVehicleTrackdir()), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
01567 TryPathReserve(Train::From(this), true, true);
01568 }
01569 }
01570 }
01571
01572
01573 void Vehicle::HandleLoading(bool mode)
01574 {
01575 switch (this->current_order.GetType()) {
01576 case OT_LOADING: {
01577 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01578
01579
01580 if (!mode && this->type != VEH_TRAIN) PayStationSharingFee(this, Station::Get(this->last_station_visited));
01581
01582
01583 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01584 (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01585
01586 this->PlayLeaveStationSound();
01587
01588 bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
01589 this->LeaveStation();
01590
01591
01592 if (!at_destination_station) return;
01593 break;
01594 }
01595
01596 case OT_DUMMY: break;
01597
01598 default: return;
01599 }
01600
01601 this->IncrementOrderIndex();
01602 }
01603
01604 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01605 {
01606 if (!CheckOwnership(this->owner)) return CMD_ERROR;
01607 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01608 if (this->IsStoppedInDepot()) return CMD_ERROR;
01609
01610 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01611 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
01612 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01613
01614
01615
01616 if (flags & DC_EXEC) {
01617 this->current_order.SetDepotOrderType(ODTF_MANUAL);
01618 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01619 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01620 }
01621 return CommandCost();
01622 }
01623
01624 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
01625 if (flags & DC_EXEC) {
01626
01627
01628 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementOrderIndex();
01629
01630 this->current_order.MakeDummy();
01631 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01632 }
01633 return CommandCost();
01634 }
01635
01636 TileIndex location;
01637 DestinationID destination;
01638 bool reverse;
01639 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};
01640 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01641
01642 if (flags & DC_EXEC) {
01643 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01644
01645 this->dest_tile = location;
01646 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01647 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01648 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01649
01650
01651 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01652
01653 if (this->type == VEH_AIRCRAFT) {
01654 Aircraft *a = Aircraft::From(this);
01655 if (a->state == FLYING && a->targetairport != destination) {
01656
01657 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01658 AircraftNextAirportPos_and_Order(a);
01659 }
01660 }
01661 }
01662
01663 return CommandCost();
01664
01665 }
01666
01667 void Vehicle::SetNext(Vehicle *next)
01668 {
01669 assert(this != next);
01670
01671 if (this->next != NULL) {
01672
01673 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01674 v->first = this->next;
01675 }
01676 this->next->previous = NULL;
01677 }
01678
01679 this->next = next;
01680
01681 if (this->next != NULL) {
01682
01683 if (this->next->previous != NULL) this->next->previous->next = NULL;
01684 this->next->previous = this;
01685 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01686 v->first = this->first;
01687 }
01688 }
01689 }
01690
01691 void Vehicle::AddToShared(Vehicle *shared_chain)
01692 {
01693 assert(this->previous_shared == NULL && this->next_shared == NULL);
01694
01695 if (!shared_chain->orders.list) {
01696 assert(shared_chain->previous_shared == NULL);
01697 assert(shared_chain->next_shared == NULL);
01698 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
01699 }
01700
01701 this->next_shared = shared_chain->next_shared;
01702 this->previous_shared = shared_chain;
01703
01704 shared_chain->next_shared = this;
01705
01706 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
01707
01708 shared_chain->orders.list->AddVehicle(this);
01709 }
01710
01711 void Vehicle::RemoveFromShared()
01712 {
01713
01714
01715 bool were_first = (this->FirstShared() == this);
01716 uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
01717
01718 this->orders.list->RemoveVehicle(this);
01719
01720 if (!were_first) {
01721
01722 this->previous_shared->next_shared = this->NextShared();
01723 }
01724
01725 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
01726
01727
01728 if (this->orders.list->GetNumVehicles() == 1) {
01729
01730 DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
01731 InvalidateVehicleOrder(this->FirstShared(), 0);
01732 } else if (were_first) {
01733
01734
01735 InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
01736 }
01737
01738 this->next_shared = NULL;
01739 this->previous_shared = NULL;
01740 }
01741
01742 void StopAllVehicles()
01743 {
01744 Vehicle *v;
01745 FOR_ALL_VEHICLES(v) {
01746
01747
01748 v->vehstatus |= VS_STOPPED;
01749 v->MarkDirty();
01750 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01751 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01752 }
01753 }
01754
01755 void VehiclesYearlyLoop()
01756 {
01757 Vehicle *v;
01758 FOR_ALL_VEHICLES(v) {
01759 if (v->IsPrimaryVehicle()) {
01760
01761 Money profit = v->GetDisplayProfitThisYear();
01762 if (v->age >= 730 && profit < 0) {
01763 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
01764 SetDParam(0, v->index);
01765 SetDParam(1, profit);
01766 AddVehicleNewsItem(
01767 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
01768 NS_ADVICE,
01769 v->index
01770 );
01771 }
01772 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
01773 }
01774
01775 v->profit_last_year = v->profit_this_year;
01776 v->profit_this_year = 0;
01777 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01778 }
01779 }
01780 }
01781
01782
01792 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
01793 {
01794 const Engine *e = Engine::GetIfValid(engine_type);
01795 assert(e != NULL);
01796
01797 switch (e->type) {
01798 case VEH_TRAIN:
01799 return (st->facilities & FACIL_TRAIN) != 0;
01800
01801 case VEH_ROAD:
01802
01803
01804
01805 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
01806
01807 case VEH_SHIP:
01808 return (st->facilities & FACIL_DOCK) != 0;
01809
01810 case VEH_AIRCRAFT:
01811 return (st->facilities & FACIL_AIRPORT) != 0 &&
01812 (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
01813
01814 default:
01815 return false;
01816 }
01817 }
01818
01825 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
01826 {
01827 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
01828
01829 return CanVehicleUseStation(v->engine_type, st);
01830 }