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