00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "gui.h"
00014 #include "roadveh.h"
00015 #include "ship.h"
00016 #include "spritecache.h"
00017 #include "timetable.h"
00018 #include "viewport_func.h"
00019 #include "news_func.h"
00020 #include "command_func.h"
00021 #include "company_func.h"
00022 #include "vehicle_gui.h"
00023 #include "train.h"
00024 #include "aircraft.h"
00025 #include "newgrf_debug.h"
00026 #include "newgrf_sound.h"
00027 #include "newgrf_station.h"
00028 #include "group.h"
00029 #include "group_gui.h"
00030 #include "strings_func.h"
00031 #include "zoom_func.h"
00032 #include "functions.h"
00033 #include "date_func.h"
00034 #include "window_func.h"
00035 #include "vehicle_func.h"
00036 #include "autoreplace_func.h"
00037 #include "autoreplace_gui.h"
00038 #include "station_base.h"
00039 #include "ai/ai.hpp"
00040 #include "depot_func.h"
00041 #include "network/network.h"
00042 #include "core/pool_func.hpp"
00043 #include "economy_base.h"
00044 #include "articulated_vehicles.h"
00045 #include "roadstop_base.h"
00046 #include "core/random_func.hpp"
00047 #include "core/backup_type.hpp"
00048 #include "order_backup.h"
00049 #include "sound_func.h"
00050 #include "effectvehicle_func.h"
00051 #include "effectvehicle_base.h"
00052 #include "vehiclelist.h"
00053 #include "tunnel_map.h"
00054 #include "depot_map.h"
00055 #include "ground_vehicle.hpp"
00056
00057 #include "table/strings.h"
00058
00059 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00060
00061 VehicleID _vehicle_id_ctr_day;
00062 VehicleID _new_vehicle_id;
00063 uint16 _returned_refit_capacity;
00064 uint16 _returned_mail_refit_capacity;
00065 byte _age_cargo_skip_counter;
00066
00067
00068
00069 VehiclePool _vehicle_pool("Vehicle");
00070 INSTANTIATE_POOL_METHODS(Vehicle)
00071
00072
00077 bool Vehicle::NeedsAutorenewing(const Company *c) const
00078 {
00079
00080
00081
00082
00083 assert(c == Company::Get(this->owner));
00084
00085 if (!c->settings.engine_renew) return false;
00086 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00087 if (this->age == 0) return false;
00088
00089 return true;
00090 }
00091
00092 void VehicleServiceInDepot(Vehicle *v)
00093 {
00094 v->date_of_last_service = _date;
00095 v->breakdowns_since_last_service = 0;
00096 v->reliability = Engine::Get(v->engine_type)->reliability;
00097 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00098 }
00099
00100 bool Vehicle::NeedsServicing() const
00101 {
00102
00103
00104 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00105
00106
00107 const Company *c = Company::Get(this->owner);
00108 if (c->settings.vehicle.servint_ispercent ?
00109 (this->reliability >= Engine::Get(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00110 (this->date_of_last_service + this->service_interval >= _date)) {
00111 return false;
00112 }
00113
00114
00115
00116 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00117 _settings_game.difficulty.vehicle_breakdowns != 0) {
00118 return true;
00119 }
00120
00121
00122
00123
00124 bool pending_replace = false;
00125 Money needed_money = c->settings.engine_renew_money;
00126 if (needed_money > c->money) return false;
00127
00128 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00129 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
00130
00131
00132 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00133
00134
00135 uint32 available_cargo_types, union_mask;
00136 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00137
00138 if (union_mask != 0) {
00139 CargoID cargo_type;
00140
00141 if (IsArticulatedVehicleCarryingDifferentCargos(v, &cargo_type)) continue;
00142
00143
00144 if (cargo_type != CT_INVALID) {
00145
00146 if (!HasBit(available_cargo_types, cargo_type)) continue;
00147 }
00148 }
00149
00150
00151
00152 pending_replace = true;
00153 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00154 if (needed_money > c->money) return false;
00155 }
00156
00157 return pending_replace;
00158 }
00159
00160 bool Vehicle::NeedsAutomaticServicing() const
00161 {
00162 if (_settings_game.order.gotodepot && this->HasDepotOrder()) return false;
00163 if (this->current_order.IsType(OT_LOADING)) return false;
00164 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00165 return NeedsServicing();
00166 }
00167
00168 uint Vehicle::Crash(bool flooded)
00169 {
00170 assert((this->vehstatus & VS_CRASHED) == 0);
00171 assert(this->Previous() == NULL);
00172
00173 uint pass = 0;
00174
00175 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00176
00177 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00178 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00179 v->vehstatus |= VS_CRASHED;
00180 MarkSingleVehicleDirty(v);
00181 }
00182
00183
00184 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00185 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
00186 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00187 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00188
00189 return pass;
00190 }
00191
00192
00201 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00202 {
00203 const Engine *e = Engine::Get(engine);
00204 uint32 grfid = e->grf_prop.grffile->grfid;
00205 GRFConfig *grfconfig = GetGRFConfig(grfid);
00206
00207 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00208 SetBit(grfconfig->grf_bugs, bug_type);
00209 SetDParamStr(0, grfconfig->GetName());
00210 SetDParam(1, engine);
00211 ShowErrorMessage(part1, part2, WL_CRITICAL);
00212 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00213 }
00214
00215
00216 char buffer[512];
00217
00218 SetDParamStr(0, grfconfig->GetName());
00219 GetString(buffer, part1, lastof(buffer));
00220 DEBUG(grf, 0, "%s", buffer + 3);
00221
00222 SetDParam(1, engine);
00223 GetString(buffer, part2, lastof(buffer));
00224 DEBUG(grf, 0, "%s", buffer + 3);
00225 }
00226
00231 Vehicle::Vehicle(VehicleType type)
00232 {
00233 this->type = type;
00234 this->coord.left = INVALID_COORD;
00235 this->group_id = DEFAULT_GROUP;
00236 this->fill_percent_te_id = INVALID_TE_ID;
00237 this->first = this;
00238 this->colourmap = PAL_NONE;
00239 }
00240
00245 byte VehicleRandomBits()
00246 {
00247 return GB(Random(), 0, 8);
00248 }
00249
00250
00251
00252 const int HASH_BITS = 7;
00253 const int HASH_SIZE = 1 << HASH_BITS;
00254 const int HASH_MASK = HASH_SIZE - 1;
00255 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00256 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00257
00258
00259
00260 const int HASH_RES = 0;
00261
00262 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00263
00264 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00265 {
00266 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00267 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00268 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00269 for (; v != NULL; v = v->next_new_hash) {
00270 Vehicle *a = proc(v, data);
00271 if (find_first && a != NULL) return a;
00272 }
00273 if (x == xu) break;
00274 }
00275 if (y == yu) break;
00276 }
00277
00278 return NULL;
00279 }
00280
00281
00293 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00294 {
00295 const int COLL_DIST = 6;
00296
00297
00298 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00299 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00300 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00301 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00302
00303 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00304 }
00305
00320 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00321 {
00322 VehicleFromPosXY(x, y, data, proc, false);
00323 }
00324
00336 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00337 {
00338 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00339 }
00340
00351 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00352 {
00353 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00354 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00355
00356 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00357 for (; v != NULL; v = v->next_new_hash) {
00358 if (v->tile != tile) continue;
00359
00360 Vehicle *a = proc(v, data);
00361 if (find_first && a != NULL) return a;
00362 }
00363
00364 return NULL;
00365 }
00366
00380 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00381 {
00382 VehicleFromPos(tile, data, proc, false);
00383 }
00384
00395 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00396 {
00397 return VehicleFromPos(tile, data, proc, true) != NULL;
00398 }
00399
00406 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00407 {
00408 byte z = *(byte*)data;
00409
00410 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00411 if (v->z_pos > z) return NULL;
00412
00413 return v;
00414 }
00415
00421 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00422 {
00423 byte z = GetTileMaxZ(tile);
00424
00425
00426
00427
00428
00429 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00430 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00431 return CommandCost();
00432 }
00433
00435 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00436 {
00437 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00438 if (v == (const Vehicle *)data) return NULL;
00439
00440 return v;
00441 }
00442
00450 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00451 {
00452
00453
00454
00455
00456 Vehicle *v = VehicleFromPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00457 if (v == NULL) v = VehicleFromPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00458
00459 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00460 return CommandCost();
00461 }
00462
00463 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00464 {
00465 TrackBits rail_bits = *(TrackBits *)data;
00466
00467 if (v->type != VEH_TRAIN) return NULL;
00468
00469 Train *t = Train::From(v);
00470 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00471
00472 return v;
00473 }
00474
00483 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00484 {
00485
00486
00487
00488
00489 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00490 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00491 return CommandCost();
00492 }
00493
00494 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00495 {
00496 Vehicle **old_hash = v->old_new_hash;
00497 Vehicle **new_hash;
00498
00499 if (remove) {
00500 new_hash = NULL;
00501 } else {
00502 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00503 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00504 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00505 }
00506
00507 if (old_hash == new_hash) return;
00508
00509
00510 if (old_hash != NULL) {
00511 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = v->prev_new_hash;
00512 *v->prev_new_hash = v->next_new_hash;
00513 }
00514
00515
00516 if (new_hash != NULL) {
00517 v->next_new_hash = *new_hash;
00518 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = &v->next_new_hash;
00519 v->prev_new_hash = new_hash;
00520 *new_hash = v;
00521 }
00522
00523
00524 v->old_new_hash = new_hash;
00525 }
00526
00527 static Vehicle *_vehicle_position_hash[0x1000];
00528
00529 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00530 {
00531 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00532
00533 Vehicle **old_hash, **new_hash;
00534 int old_x = v->coord.left;
00535 int old_y = v->coord.top;
00536
00537 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00538 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00539
00540 if (old_hash == new_hash) return;
00541
00542
00543 if (old_hash != NULL) {
00544 if (v->next_hash != NULL) v->next_hash->prev_hash = v->prev_hash;
00545 *v->prev_hash = v->next_hash;
00546 }
00547
00548
00549 if (new_hash != NULL) {
00550 v->next_hash = *new_hash;
00551 if (v->next_hash != NULL) v->next_hash->prev_hash = &v->next_hash;
00552 v->prev_hash = new_hash;
00553 *new_hash = v;
00554 }
00555 }
00556
00557 void ResetVehiclePosHash()
00558 {
00559 Vehicle *v;
00560 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00561 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00562 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00563 }
00564
00565 void ResetVehicleColourMap()
00566 {
00567 Vehicle *v;
00568 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00569 }
00570
00575 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00576 static AutoreplaceMap _vehicles_to_autoreplace;
00577
00578 void InitializeVehicles()
00579 {
00580 _vehicle_pool.CleanPool();
00581 _cargo_payment_pool.CleanPool();
00582
00583 _age_cargo_skip_counter = 1;
00584
00585 _vehicles_to_autoreplace.Reset();
00586 ResetVehiclePosHash();
00587 }
00588
00589 uint CountVehiclesInChain(const Vehicle *v)
00590 {
00591 uint count = 0;
00592 do count++; while ((v = v->Next()) != NULL);
00593 return count;
00594 }
00595
00601 void CountCompanyVehicles(CompanyID cid, uint counts[4])
00602 {
00603 for (uint i = 0; i < 4; i++) counts[i] = 0;
00604
00605 const Vehicle *v;
00606 FOR_ALL_VEHICLES(v) {
00607 if (v->owner == cid && v->IsPrimaryVehicle()) counts[v->type]++;
00608 }
00609 }
00610
00615 bool Vehicle::IsEngineCountable() const
00616 {
00617 switch (this->type) {
00618 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00619 case VEH_TRAIN:
00620 return !Train::From(this)->IsArticulatedPart() &&
00621 !Train::From(this)->IsRearDualheaded();
00622 case VEH_ROAD: return RoadVehicle::From(this)->IsRoadVehFront();
00623 case VEH_SHIP: return true;
00624 default: return false;
00625 }
00626 }
00627
00635 void Vehicle::HandlePathfindingResult(bool path_found)
00636 {
00637 if (path_found) {
00638
00639 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00640
00641
00642 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00643
00644 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00645 return;
00646 }
00647
00648
00649 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00650
00651
00652 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00653
00654 AI::NewEvent(this->owner, new AIEventVehicleLost(this->index));
00655 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00656 SetDParam(0, this->index);
00657 AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00658 }
00659 }
00660
00661 void Vehicle::PreDestructor()
00662 {
00663 if (CleaningPool()) return;
00664
00665 if (Station::IsValidID(this->last_station_visited)) {
00666 Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00667
00668 HideFillingPercent(&this->fill_percent_te_id);
00669
00670 delete this->cargo_payment;
00671 }
00672
00673 if (this->IsEngineCountable()) {
00674 Company::Get(this->owner)->num_engines[this->engine_type]--;
00675 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00676
00677 DeleteGroupHighlightOfVehicle(this);
00678 if (Group::IsValidID(this->group_id)) Group::Get(this->group_id)->num_engines[this->engine_type]--;
00679 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00680 }
00681
00682 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00683 Aircraft *a = Aircraft::From(this);
00684 Station *st = GetTargetAirportIfValid(a);
00685 if (st != NULL) {
00686 const AirportFTA *layout = st->airport.GetFTA()->layout;
00687 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00688 }
00689 }
00690
00691
00692 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00693 RoadVehicle *v = RoadVehicle::From(this);
00694 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00695
00696 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00697 }
00698 }
00699
00700 if (this->Previous() == NULL) {
00701 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00702 }
00703
00704 if (this->IsPrimaryVehicle()) {
00705 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00706 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00707 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00708 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00709 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00710 SetWindowDirty(WC_COMPANY, this->owner);
00711 OrderBackup::ClearVehicle(this);
00712 }
00713 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00714
00715 this->cargo.Truncate(0);
00716 DeleteVehicleOrders(this);
00717 DeleteDepotHighlightOfVehicle(this);
00718
00719 extern void StopGlobalFollowVehicle(const Vehicle *v);
00720 StopGlobalFollowVehicle(this);
00721
00722 ReleaseDisastersTargetingVehicle(this->index);
00723 }
00724
00725 Vehicle::~Vehicle()
00726 {
00727 free(this->name);
00728
00729 if (CleaningPool()) return;
00730
00731
00732
00733 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00734
00735 Vehicle *v = this->Next();
00736 this->SetNext(NULL);
00737
00738 delete v;
00739
00740 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00741 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00742 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00743 }
00744
00749 void VehicleEnteredDepotThisTick(Vehicle *v)
00750 {
00751
00752 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00753
00754
00755
00756
00757
00758
00759 v->vehstatus |= VS_STOPPED;
00760 }
00761
00767 static void RunVehicleDayProc()
00768 {
00769 if (_game_mode != GM_NORMAL) return;
00770
00771
00772 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00773 Vehicle *v = Vehicle::Get(i);
00774 if (v == NULL) continue;
00775
00776
00777 if ((v->day_counter & 0x1F) == 0) {
00778 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00779 if (callback != CALLBACK_FAILED) {
00780 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00781 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00782 }
00783 }
00784
00785
00786 v->OnNewDay();
00787 }
00788 }
00789
00790 void CallVehicleTicks()
00791 {
00792 _vehicles_to_autoreplace.Clear();
00793
00794 _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
00795
00796 RunVehicleDayProc();
00797
00798 Station *st;
00799 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00800
00801 Vehicle *v;
00802 FOR_ALL_VEHICLES(v) {
00803
00804 if (!v->Tick()) {
00805 assert(Vehicle::Get(vehicle_index) == NULL);
00806 continue;
00807 }
00808
00809 assert(Vehicle::Get(vehicle_index) == v);
00810
00811 switch (v->type) {
00812 default: break;
00813
00814 case VEH_TRAIN:
00815 case VEH_ROAD:
00816 case VEH_AIRCRAFT:
00817 case VEH_SHIP:
00818 if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
00819
00820 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00821 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00822 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsRoadVehFront()) continue;
00823
00824 v->motion_counter += v->cur_speed;
00825
00826 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00827
00828
00829 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00830 }
00831 }
00832
00833 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00834 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00835 v = it->first;
00836
00837 cur_company.Change(v->owner);
00838
00839
00840
00841
00842 if (it->second) v->vehstatus &= ~VS_STOPPED;
00843
00844
00845 int x = v->x_pos;
00846 int y = v->y_pos;
00847 int z = v->z_pos;
00848
00849 const Company *c = Company::Get(_current_company);
00850 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00851 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00852 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00853
00854 if (!IsLocalCompany()) continue;
00855
00856 if (res.Succeeded()) {
00857 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00858 continue;
00859 }
00860
00861 StringID error_message = res.GetErrorMessage();
00862 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00863
00864 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00865
00866 StringID message;
00867 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00868 message = error_message;
00869 } else {
00870 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00871 }
00872
00873 SetDParam(0, v->index);
00874 SetDParam(1, error_message);
00875 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00876 }
00877
00878 cur_company.Restore();
00879 }
00880
00885 static void DoDrawVehicle(const Vehicle *v)
00886 {
00887 SpriteID image = v->cur_image;
00888 PaletteID pal = PAL_NONE;
00889
00890 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00891
00892 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00893 v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00894 }
00895
00896 void ViewportAddVehicles(DrawPixelInfo *dpi)
00897 {
00898
00899 const int l = dpi->left;
00900 const int r = dpi->left + dpi->width;
00901 const int t = dpi->top;
00902 const int b = dpi->top + dpi->height;
00903
00904
00905 int xl, xu, yl, yu;
00906
00907 if (dpi->width + 70 < (1 << (7 + 6))) {
00908 xl = GB(l - 70, 7, 6);
00909 xu = GB(r, 7, 6);
00910 } else {
00911
00912 xl = 0;
00913 xu = 0x3F;
00914 }
00915
00916 if (dpi->height + 70 < (1 << (6 + 6))) {
00917 yl = GB(t - 70, 6, 6) << 6;
00918 yu = GB(b, 6, 6) << 6;
00919 } else {
00920
00921 yl = 0;
00922 yu = 0x3F << 6;
00923 }
00924
00925 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00926 for (int x = xl;; x = (x + 1) & 0x3F) {
00927 const Vehicle *v = _vehicle_position_hash[x + y];
00928
00929 while (v != NULL) {
00930 if (!(v->vehstatus & VS_HIDDEN) &&
00931 l <= v->coord.right &&
00932 t <= v->coord.bottom &&
00933 r >= v->coord.left &&
00934 b >= v->coord.top) {
00935 DoDrawVehicle(v);
00936 }
00937 v = v->next_hash;
00938 }
00939
00940 if (x == xu) break;
00941 }
00942
00943 if (y == yu) break;
00944 }
00945 }
00946
00947 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00948 {
00949 Vehicle *found = NULL, *v;
00950 uint dist, best_dist = UINT_MAX;
00951
00952 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00953
00954 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00955 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00956
00957 FOR_ALL_VEHICLES(v) {
00958 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00959 x >= v->coord.left && x <= v->coord.right &&
00960 y >= v->coord.top && y <= v->coord.bottom) {
00961
00962 dist = max(
00963 abs(((v->coord.left + v->coord.right) >> 1) - x),
00964 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00965 );
00966
00967 if (dist < best_dist) {
00968 found = v;
00969 best_dist = dist;
00970 }
00971 }
00972 }
00973
00974 return found;
00975 }
00976
00977 void DecreaseVehicleValue(Vehicle *v)
00978 {
00979 v->value -= v->value >> 8;
00980 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00981 }
00982
00983 static const byte _breakdown_chance[64] = {
00984 3, 3, 3, 3, 3, 3, 3, 3,
00985 4, 4, 5, 5, 6, 6, 7, 7,
00986 8, 8, 9, 9, 10, 10, 11, 11,
00987 12, 13, 13, 13, 13, 14, 15, 16,
00988 17, 19, 21, 25, 28, 31, 34, 37,
00989 40, 44, 48, 52, 56, 60, 64, 68,
00990 72, 80, 90, 100, 110, 120, 130, 140,
00991 150, 170, 190, 210, 230, 250, 250, 250,
00992 };
00993
00994 void CheckVehicleBreakdown(Vehicle *v)
00995 {
00996 int rel, rel_old;
00997
00998
00999 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01000 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01001
01002 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01003 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01004 v->cur_speed < 5 || _game_mode == GM_MENU) {
01005 return;
01006 }
01007
01008 uint32 r = Random();
01009
01010
01011 int chance = v->breakdown_chance + 1;
01012 if (Chance16I(1, 25, r)) chance += 25;
01013 v->breakdown_chance = min(255, chance);
01014
01015
01016 rel = v->reliability;
01017 if (v->type == VEH_SHIP) rel += 0x6666;
01018
01019
01020 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01021
01022
01023 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01024 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01025 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01026 v->breakdown_chance = 0;
01027 }
01028 }
01029
01030 bool Vehicle::HandleBreakdown()
01031 {
01032
01033
01034
01035
01036
01037 switch (this->breakdown_ctr) {
01038 case 0:
01039 return false;
01040
01041 case 2:
01042 this->breakdown_ctr = 1;
01043
01044 if (this->breakdowns_since_last_service != 255) {
01045 this->breakdowns_since_last_service++;
01046 }
01047
01048 this->MarkDirty();
01049 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01050 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01051
01052 if (this->type == VEH_AIRCRAFT) {
01053
01054 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01055 } else {
01056 this->cur_speed = 0;
01057
01058 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01059 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01060 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01061 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01062 }
01063
01064 if (!(this->vehstatus & VS_HIDDEN)) {
01065 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01066 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01067 }
01068 }
01069
01070 case 1:
01071
01072 if (this->type == VEH_AIRCRAFT) return false;
01073
01074
01075 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01076 if (--this->breakdown_delay == 0) {
01077 this->breakdown_ctr = 0;
01078 this->MarkDirty();
01079 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01080 }
01081 }
01082 return true;
01083
01084 default:
01085 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01086 return false;
01087 }
01088 }
01089
01094 void AgeVehicle(Vehicle *v)
01095 {
01096 if (v->age < MAX_DAY) v->age++;
01097
01098 int age = v->age - v->max_age;
01099 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01100 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01101 v->reliability_spd_dec <<= 1;
01102 }
01103
01104 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01105
01106
01107 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01108
01109
01110 if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
01111
01112 StringID str;
01113 if (age == -DAYS_IN_LEAP_YEAR) {
01114 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01115 } else if (age == 0) {
01116 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01117 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01118 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01119 } else {
01120 return;
01121 }
01122
01123 SetDParam(0, v->index);
01124 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01125 }
01126
01133 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01134 {
01135 int count = 0;
01136 int max = 0;
01137 int cars = 0;
01138 int unloading = 0;
01139 bool loading = false;
01140
01141 const Vehicle *u = v;
01142
01143 const Station *st = Station::GetIfValid(v->last_station_visited);
01144 assert(colour == NULL || st != NULL);
01145
01146
01147 for (; v != NULL; v = v->Next()) {
01148 count += v->cargo.Count();
01149 max += v->cargo_cap;
01150 if (v->cargo_cap != 0 && colour != NULL) {
01151 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01152 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01153 cars++;
01154 }
01155 }
01156
01157 if (colour != NULL) {
01158 if (unloading == 0 && loading) {
01159 *colour = STR_PERCENT_UP;
01160 } else if (cars == unloading || !loading) {
01161 *colour = STR_PERCENT_DOWN;
01162 } else {
01163 *colour = STR_PERCENT_UP_DOWN;
01164 }
01165 }
01166
01167
01168 if (max == 0) return 100;
01169
01170
01171 return (count * 100) / max;
01172 }
01173
01174 void VehicleEnterDepot(Vehicle *v)
01175 {
01176
01177 assert(v == v->First());
01178
01179 switch (v->type) {
01180 case VEH_TRAIN: {
01181 Train *t = Train::From(v);
01182 SetWindowClassesDirty(WC_TRAINS_LIST);
01183
01184 SetDepotReservation(t->tile, false);
01185 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01186
01187 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01188 t->wait_counter = 0;
01189 t->force_proceed = TFP_NONE;
01190 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01191 t->ConsistChanged(true);
01192 break;
01193 }
01194
01195 case VEH_ROAD:
01196 SetWindowClassesDirty(WC_ROADVEH_LIST);
01197 break;
01198
01199 case VEH_SHIP: {
01200 SetWindowClassesDirty(WC_SHIPS_LIST);
01201 Ship *ship = Ship::From(v);
01202 ship->state = TRACK_BIT_DEPOT;
01203 ship->UpdateCache();
01204 ship->UpdateViewport(true, true);
01205 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01206 break;
01207 }
01208
01209 case VEH_AIRCRAFT:
01210 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01211 HandleAircraftEnterHangar(Aircraft::From(v));
01212 break;
01213 default: NOT_REACHED();
01214 }
01215 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01216
01217 if (v->type != VEH_TRAIN) {
01218
01219
01220 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01221 }
01222 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01223
01224 v->vehstatus |= VS_HIDDEN;
01225 v->cur_speed = 0;
01226
01227 VehicleServiceInDepot(v);
01228
01229 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01230
01231 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01232 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01233
01234 const Order *real_order = v->GetNextManualOrder(v->cur_order_index);
01235 Order t = v->current_order;
01236 v->current_order.MakeDummy();
01237
01238
01239
01240 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01241 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01242 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01243
01244 return;
01245 }
01246
01247 if (t.IsRefit()) {
01248 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01249 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01250 cur_company.Restore();
01251
01252 if (cost.Failed()) {
01253 _vehicles_to_autoreplace[v] = false;
01254 if (v->owner == _local_company) {
01255
01256 SetDParam(0, v->index);
01257 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01258 }
01259 } else if (cost.GetCost() != 0) {
01260 v->profit_this_year -= cost.GetCost() << 8;
01261 if (v->owner == _local_company) {
01262 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01263 }
01264 }
01265 }
01266
01267 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01268
01269 UpdateVehicleTimetable(v, true);
01270 v->IncrementOrderIndex();
01271 }
01272 if (t.GetDepotActionType() & ODATFB_HALT) {
01273
01274 _vehicles_to_autoreplace[v] = false;
01275 if (v->owner == _local_company) {
01276 SetDParam(0, v->index);
01277 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01278 }
01279 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01280 }
01281 }
01282 }
01283
01284
01292 void VehicleMove(Vehicle *v, bool update_viewport)
01293 {
01294 int img = v->cur_image;
01295 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01296 const Sprite *spr = GetSprite(img, ST_NORMAL);
01297
01298 pt.x += spr->x_offs;
01299 pt.y += spr->y_offs;
01300
01301 UpdateVehiclePosHash(v, pt.x, pt.y);
01302
01303 Rect old_coord = v->coord;
01304 v->coord.left = pt.x;
01305 v->coord.top = pt.y;
01306 v->coord.right = pt.x + spr->width + 2;
01307 v->coord.bottom = pt.y + spr->height + 2;
01308
01309 if (update_viewport) {
01310 MarkAllViewportsDirty(
01311 min(old_coord.left, v->coord.left),
01312 min(old_coord.top, v->coord.top),
01313 max(old_coord.right, v->coord.right) + 1,
01314 max(old_coord.bottom, v->coord.bottom) + 1
01315 );
01316 }
01317 }
01318
01327 void MarkSingleVehicleDirty(const Vehicle *v)
01328 {
01329 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01330 }
01331
01337 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01338 {
01339 static const int8 _delta_coord[16] = {
01340 -1,-1,-1, 0, 1, 1, 1, 0,
01341 -1, 0, 1, 1, 1, 0,-1,-1,
01342 };
01343
01344 int x = v->x_pos + _delta_coord[v->direction];
01345 int y = v->y_pos + _delta_coord[v->direction + 8];
01346
01347 GetNewVehiclePosResult gp;
01348 gp.x = x;
01349 gp.y = y;
01350 gp.old_tile = v->tile;
01351 gp.new_tile = TileVirtXY(x, y);
01352 return gp;
01353 }
01354
01355 static const Direction _new_direction_table[] = {
01356 DIR_N, DIR_NW, DIR_W,
01357 DIR_NE, DIR_SE, DIR_SW,
01358 DIR_E, DIR_SE, DIR_S
01359 };
01360
01361 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01362 {
01363 int i = 0;
01364
01365 if (y >= v->y_pos) {
01366 if (y != v->y_pos) i += 3;
01367 i += 3;
01368 }
01369
01370 if (x >= v->x_pos) {
01371 if (x != v->x_pos) i++;
01372 i++;
01373 }
01374
01375 Direction dir = v->direction;
01376
01377 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01378 if (dirdiff == DIRDIFF_SAME) return dir;
01379 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01380 }
01381
01391 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01392 {
01393 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01394 }
01395
01396 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01397 {
01398
01399 const Vehicle *v;
01400 FOR_ALL_VEHICLES(v) {
01401 if (v->type == type && v->owner == owner) {
01402 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01403 }
01404 }
01405
01406 if (this->maxid == 0) return;
01407
01408
01409
01410
01411 this->cache = CallocT<bool>(this->maxid + 2);
01412
01413
01414 FOR_ALL_VEHICLES(v) {
01415 if (v->type == type && v->owner == owner) {
01416 this->cache[v->unitnumber] = true;
01417 }
01418 }
01419 }
01420
01421 UnitID FreeUnitIDGenerator::NextID()
01422 {
01423 if (this->maxid <= this->curid) return ++this->curid;
01424
01425 while (this->cache[++this->curid]) { }
01426
01427 return this->curid;
01428 }
01429
01435 UnitID GetFreeUnitNumber(VehicleType type)
01436 {
01437
01438 uint max_veh;
01439 switch (type) {
01440 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01441 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01442 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01443 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01444 default: NOT_REACHED();
01445 }
01446
01447 uint amounts[4];
01448 CountCompanyVehicles(_current_company, amounts);
01449 assert((uint)type < lengthof(amounts));
01450 if (amounts[type] >= max_veh) return UINT16_MAX;
01451
01452 FreeUnitIDGenerator gen(type, _current_company);
01453
01454 return gen.NextID();
01455 }
01456
01457
01466 bool CanBuildVehicleInfrastructure(VehicleType type)
01467 {
01468 assert(IsCompanyBuildableVehicleType(type));
01469
01470 if (!Company::IsValidID(_local_company)) return false;
01471 if (_settings_client.gui.always_build_infrastructure) return true;
01472
01473 UnitID max;
01474 switch (type) {
01475 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01476 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01477 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01478 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01479 default: NOT_REACHED();
01480 }
01481
01482
01483 if (max > 0) {
01484
01485 const Engine *e;
01486 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01487 if (HasBit(e->company_avail, _local_company)) return true;
01488 }
01489 return false;
01490 }
01491
01492
01493 const Vehicle *v;
01494 FOR_ALL_VEHICLES(v) {
01495 if (v->owner == _local_company && v->type == type) return true;
01496 }
01497
01498 return false;
01499 }
01500
01501
01509 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01510 {
01511 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01512 const Engine *e = Engine::Get(engine_type);
01513 switch (e->type) {
01514 default: NOT_REACHED();
01515 case VEH_TRAIN:
01516 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (Train::From(v)->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01517
01518
01519 engine_type = parent_engine_type;
01520 e = Engine::Get(engine_type);
01521
01522 }
01523
01524 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01525 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01526 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01527 if (!CargoSpec::Get(cargo_type)->is_freight) {
01528 if (parent_engine_type == INVALID_ENGINE) {
01529 return LS_PASSENGER_WAGON_STEAM;
01530 } else {
01531 switch (RailVehInfo(parent_engine_type)->engclass) {
01532 default: NOT_REACHED();
01533 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01534 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01535 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01536 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01537 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01538 }
01539 }
01540 } else {
01541 return LS_FREIGHT_WAGON;
01542 }
01543 } else {
01544 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01545
01546 switch (e->u.rail.engclass) {
01547 default: NOT_REACHED();
01548 case EC_STEAM: return LS_STEAM;
01549 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01550 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01551 case EC_MONORAIL: return LS_MONORAIL;
01552 case EC_MAGLEV: return LS_MAGLEV;
01553 }
01554 }
01555
01556 case VEH_ROAD:
01557
01558 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01559 engine_type = parent_engine_type;
01560 e = Engine::Get(engine_type);
01561 cargo_type = v->First()->cargo_type;
01562 }
01563 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01564 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01565
01566
01567 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01568
01569 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01570 } else {
01571
01572 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01573 }
01574
01575 case VEH_SHIP:
01576 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01577 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01578 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01579
01580 case VEH_AIRCRAFT:
01581 switch (e->u.air.subtype) {
01582 case AIR_HELI: return LS_HELICOPTER;
01583 case AIR_CTOL: return LS_SMALL_PLANE;
01584 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01585 default: NOT_REACHED();
01586 }
01587 }
01588 }
01589
01599 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01600 {
01601 const Company *c = Company::Get(company);
01602 LiveryScheme scheme = LS_DEFAULT;
01603
01604
01605
01606 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01607
01608 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01609
01610
01611 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01612 }
01613
01614 return &c->livery[scheme];
01615 }
01616
01617
01618 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01619 {
01620 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01621
01622
01623 if (map != PAL_NONE) return map;
01624
01625 const Engine *e = Engine::Get(engine_type);
01626
01627
01628 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01629 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01630
01631 if (callback != CALLBACK_FAILED) {
01632 assert_compile(PAL_NONE == 0);
01633 map = GB(callback, 0, 14);
01634
01635
01636 if (!HasBit(callback, 14)) {
01637
01638 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01639 return map;
01640 }
01641 }
01642 }
01643
01644 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01645
01646 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01647
01648
01649 if (!Company::IsValidID(company)) return map;
01650
01651 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01652
01653 map += livery->colour1;
01654 if (twocc) map += livery->colour2 * 16;
01655
01656
01657 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01658 return map;
01659 }
01660
01661 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01662 {
01663 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01664 }
01665
01666 PaletteID GetVehiclePalette(const Vehicle *v)
01667 {
01668 if (v->IsGroundVehicle()) {
01669 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01670 }
01671
01672 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01673 }
01674
01683 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01684 {
01685 if (mail_capacity != NULL) *mail_capacity = 0;
01686 const Engine *e = Engine::Get(v->engine_type);
01687
01688 if (!e->CanCarryCargo()) return 0;
01689
01690 if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01691 *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01692 }
01693 CargoID default_cargo = e->GetDefaultCargoType();
01694
01695
01696
01697 if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01698 (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01699 uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01700 if (callback != CALLBACK_FAILED) return callback;
01701 }
01702
01703
01704 uint capacity;
01705 switch (e->type) {
01706 case VEH_TRAIN: capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY, e->u.rail.capacity); break;
01707 case VEH_ROAD: capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
01708 case VEH_SHIP: capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY, e->u.ship.capacity); break;
01709 case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
01710 default: NOT_REACHED();
01711 }
01712
01713
01714
01715 if (e->type != VEH_SHIP) {
01716 if (e->type == VEH_AIRCRAFT) {
01717 if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01718 capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01719 }
01720 if (v->cargo_type == CT_MAIL) return capacity;
01721 } else {
01722 switch (default_cargo) {
01723 case CT_PASSENGERS: break;
01724 case CT_MAIL:
01725 case CT_GOODS: capacity *= 2; break;
01726 default: capacity *= 4; break;
01727 }
01728 }
01729 switch (v->cargo_type) {
01730 case CT_PASSENGERS: break;
01731 case CT_MAIL:
01732 case CT_GOODS: capacity /= 2; break;
01733 default: capacity /= 4; break;
01734 }
01735 }
01736
01737 return capacity;
01738 }
01739
01740
01741 void Vehicle::BeginLoading()
01742 {
01743 assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01744
01745 if (this->current_order.IsType(OT_GOTO_STATION) &&
01746 this->current_order.GetDestination() == this->last_station_visited) {
01747
01748 const Order *order = this->GetOrder(this->cur_order_index);
01749 while (order != NULL && order->IsType(OT_AUTOMATIC)) {
01750
01751 order = order->next;
01752 DeleteOrder(this, this->cur_order_index);
01753 }
01754
01755
01756 this->current_order.MakeLoading(true);
01757 UpdateVehicleTimetable(this, true);
01758
01759
01760
01761
01762
01763
01764 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01765
01766 } else {
01767
01768
01769 Order *in_list = this->GetOrder(this->cur_order_index);
01770 if ((this->orders.list == NULL || this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID) &&
01771 ((in_list == NULL && this->cur_order_index == 0) ||
01772 (in_list != NULL && (!in_list->IsType(OT_AUTOMATIC) ||
01773 in_list->GetDestination() != this->last_station_visited)))) {
01774 Order *auto_order = new Order();
01775 auto_order->MakeAutomatic(this->last_station_visited);
01776 InsertOrder(this, auto_order, this->cur_order_index);
01777 if (this->cur_order_index > 0) --this->cur_order_index;
01778 }
01779 this->current_order.MakeLoading(false);
01780 }
01781
01782 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01783
01784 PrepareUnload(this);
01785
01786 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01787 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01788 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01789 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01790
01791 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01792 this->cur_speed = 0;
01793 this->MarkDirty();
01794 }
01795
01796 void Vehicle::LeaveStation()
01797 {
01798 assert(current_order.IsType(OT_LOADING));
01799
01800 delete this->cargo_payment;
01801
01802
01803 if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01804
01805 current_order.MakeLeaveStation();
01806 Station *st = Station::Get(this->last_station_visited);
01807 st->loading_vehicles.remove(this);
01808
01809 HideFillingPercent(&this->fill_percent_te_id);
01810
01811 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01812
01813 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
01814
01815 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
01816 }
01817 }
01818
01819
01820 void Vehicle::HandleLoading(bool mode)
01821 {
01822 switch (this->current_order.GetType()) {
01823 case OT_LOADING: {
01824 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01825
01826
01827 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01828 (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01829
01830 this->PlayLeaveStationSound();
01831
01832 this->LeaveStation();
01833
01834 break;
01835 }
01836
01837 case OT_DUMMY: break;
01838
01839 default: return;
01840 }
01841
01842 this->IncrementOrderIndex();
01843 }
01844
01845 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01846 {
01847 CommandCost ret = CheckOwnership(this->owner);
01848 if (ret.Failed()) return ret;
01849
01850 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01851 if (this->IsStoppedInDepot()) return CMD_ERROR;
01852
01853 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01854 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
01855 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01856
01857
01858
01859 if (flags & DC_EXEC) {
01860 this->current_order.SetDepotOrderType(ODTF_MANUAL);
01861 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01862 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01863 }
01864 return CommandCost();
01865 }
01866
01867 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
01868 if (flags & DC_EXEC) {
01869
01870
01871 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementOrderIndex();
01872
01873 this->current_order.MakeDummy();
01874 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01875 }
01876 return CommandCost();
01877 }
01878
01879 TileIndex location;
01880 DestinationID destination;
01881 bool reverse;
01882 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};
01883 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01884
01885 if (flags & DC_EXEC) {
01886 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01887
01888 this->dest_tile = location;
01889 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01890 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01891 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01892
01893
01894 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01895
01896 if (this->type == VEH_AIRCRAFT) {
01897 Aircraft *a = Aircraft::From(this);
01898 if (a->state == FLYING && a->targetairport != destination) {
01899
01900 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01901 AircraftNextAirportPos_and_Order(a);
01902 }
01903 }
01904 }
01905
01906 return CommandCost();
01907
01908 }
01909
01910 void Vehicle::UpdateVisualEffect(bool allow_power_change)
01911 {
01912 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
01913 const Engine *e = Engine::Get(this->engine_type);
01914
01915
01916 byte visual_effect;
01917 switch (e->type) {
01918 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
01919 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
01920 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
01921 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
01922 }
01923
01924
01925 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
01926 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
01927
01928 if (callback != CALLBACK_FAILED) {
01929 callback = GB(callback, 0, 8);
01930
01931
01932 if (callback == VE_DEFAULT) {
01933 assert(HasBit(callback, VE_DISABLE_EFFECT));
01934 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
01935 }
01936 visual_effect = callback;
01937 }
01938 }
01939
01940
01941 if (visual_effect == VE_DEFAULT ||
01942 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
01943
01944
01945 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
01946 if (visual_effect == VE_DEFAULT) {
01947 visual_effect = 1 << VE_DISABLE_EFFECT;
01948 } else {
01949 SetBit(visual_effect, VE_DISABLE_EFFECT);
01950 }
01951 } else {
01952 if (visual_effect == VE_DEFAULT) {
01953
01954 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
01955 }
01956 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
01957 }
01958 }
01959
01960 this->vcache.cached_vis_effect = visual_effect;
01961
01962 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
01963 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
01964 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
01965 }
01966 }
01967
01968 static const int8 _vehicle_smoke_pos[8] = {
01969 1, 1, 1, 0, -1, -1, -1, 0
01970 };
01971
01972 void Vehicle::ShowVisualEffect() const
01973 {
01974 assert(this->IsPrimaryVehicle());
01975 bool sound = false;
01976
01977
01978
01979
01980
01981
01982 if (_settings_game.vehicle.smoke_amount == 0 ||
01983 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
01984 this->cur_speed < 2) {
01985 return;
01986 }
01987 if (this->type == VEH_TRAIN) {
01988 const Train *t = Train::From(this);
01989
01990
01991
01992
01993 if (HasBit(t->flags, VRF_REVERSING) ||
01994 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
01995 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
01996 return;
01997 }
01998 }
01999
02000 const Vehicle *v = this;
02001
02002 do {
02003 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02004 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02005 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02006
02007
02008
02009
02010
02011
02012
02013 if (disable_effect ||
02014 v->vehstatus & VS_HIDDEN ||
02015 IsDepotTile(v->tile) ||
02016 IsTunnelTile(v->tile) ||
02017 (v->type == VEH_TRAIN &&
02018 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02019 continue;
02020 }
02021
02022 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02023 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02024
02025 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02026 x = -x;
02027 y = -y;
02028 }
02029
02030 switch (effect_type) {
02031 case VE_TYPE_STEAM:
02032
02033
02034
02035
02036
02037 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02038 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02039 sound = true;
02040 }
02041 break;
02042
02043 case VE_TYPE_DIESEL: {
02044
02045
02046
02047
02048
02049
02050
02051
02052
02053
02054
02055 int power_weight_effect = 0;
02056 if (v->type == VEH_TRAIN) {
02057 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02058 }
02059 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02060 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02061 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02062 sound = true;
02063 }
02064 break;
02065 }
02066
02067 case VE_TYPE_ELECTRIC:
02068
02069
02070
02071
02072
02073
02074 if (GB(v->tick_counter, 0, 2) == 0 &&
02075 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02076 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02077 sound = true;
02078 }
02079 break;
02080
02081 default:
02082 break;
02083 }
02084 } while ((v = v->Next()) != NULL);
02085
02086 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02087 }
02088
02089 void Vehicle::SetNext(Vehicle *next)
02090 {
02091 assert(this != next);
02092
02093 if (this->next != NULL) {
02094
02095 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02096 v->first = this->next;
02097 }
02098 this->next->previous = NULL;
02099 }
02100
02101 this->next = next;
02102
02103 if (this->next != NULL) {
02104
02105 if (this->next->previous != NULL) this->next->previous->next = NULL;
02106 this->next->previous = this;
02107 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02108 v->first = this->first;
02109 }
02110 }
02111 }
02112
02113 void Vehicle::AddToShared(Vehicle *shared_chain)
02114 {
02115 assert(this->previous_shared == NULL && this->next_shared == NULL);
02116
02117 if (!shared_chain->orders.list) {
02118 assert(shared_chain->previous_shared == NULL);
02119 assert(shared_chain->next_shared == NULL);
02120 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02121 }
02122
02123 this->next_shared = shared_chain->next_shared;
02124 this->previous_shared = shared_chain;
02125
02126 shared_chain->next_shared = this;
02127
02128 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02129
02130 shared_chain->orders.list->AddVehicle(this);
02131 }
02132
02133 void Vehicle::RemoveFromShared()
02134 {
02135
02136
02137 bool were_first = (this->FirstShared() == this);
02138 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02139
02140 this->orders.list->RemoveVehicle(this);
02141
02142 if (!were_first) {
02143
02144 this->previous_shared->next_shared = this->NextShared();
02145 }
02146
02147 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02148
02149
02150 if (this->orders.list->GetNumVehicles() == 1) {
02151
02152 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02153 InvalidateVehicleOrder(this->FirstShared(), 0);
02154 } else if (were_first) {
02155
02156
02157 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02158 }
02159
02160 this->next_shared = NULL;
02161 this->previous_shared = NULL;
02162 }
02163
02169 Order *Vehicle::GetNextManualOrder(int index) const
02170 {
02171 Order *order = this->GetOrder(index);
02172 while(order != NULL && order->IsType(OT_AUTOMATIC)) {
02173 order = order->next;
02174 }
02175 return order;
02176 }
02177
02178 void StopAllVehicles()
02179 {
02180 Vehicle *v;
02181 FOR_ALL_VEHICLES(v) {
02182
02183
02184 v->vehstatus |= VS_STOPPED;
02185 v->MarkDirty();
02186 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
02187 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
02188 }
02189 }
02190
02191 void VehiclesYearlyLoop()
02192 {
02193 Vehicle *v;
02194 FOR_ALL_VEHICLES(v) {
02195 if (v->IsPrimaryVehicle()) {
02196
02197 Money profit = v->GetDisplayProfitThisYear();
02198 if (v->age >= 730 && profit < 0) {
02199 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02200 SetDParam(0, v->index);
02201 SetDParam(1, profit);
02202 AddVehicleNewsItem(
02203 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02204 NS_ADVICE,
02205 v->index
02206 );
02207 }
02208 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02209 }
02210
02211 v->profit_last_year = v->profit_this_year;
02212 v->profit_this_year = 0;
02213 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02214 }
02215 }
02216 }
02217
02218
02228 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02229 {
02230 const Engine *e = Engine::GetIfValid(engine_type);
02231 assert(e != NULL);
02232
02233 switch (e->type) {
02234 case VEH_TRAIN:
02235 return (st->facilities & FACIL_TRAIN) != 0;
02236
02237 case VEH_ROAD:
02238
02239
02240
02241 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02242
02243 case VEH_SHIP:
02244 return (st->facilities & FACIL_DOCK) != 0;
02245
02246 case VEH_AIRCRAFT:
02247 return (st->facilities & FACIL_AIRPORT) != 0 &&
02248 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02249
02250 default:
02251 return false;
02252 }
02253 }
02254
02261 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02262 {
02263 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02264
02265 return CanVehicleUseStation(v->engine_type, st);
02266 }
02267
02273 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02274 {
02275 assert(this->IsGroundVehicle());
02276 if (this->type == VEH_TRAIN) {
02277 return &Train::From(this)->gcache;
02278 } else {
02279 return &RoadVehicle::From(this)->gcache;
02280 }
02281 }
02282
02288 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02289 {
02290 assert(this->IsGroundVehicle());
02291 if (this->type == VEH_TRAIN) {
02292 return &Train::From(this)->gcache;
02293 } else {
02294 return &RoadVehicle::From(this)->gcache;
02295 }
02296 }
02297
02306 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02307 {
02308 if (v->type == VEH_TRAIN) {
02309 Train *u = Train::From(v);
02310
02311 if (u->IsArticulatedPart()) {
02312 u = u->GetFirstEnginePart();
02313 while (u->index != v->index) {
02314 set.Include(u->index);
02315 u = u->GetNextArticPart();
02316 }
02317 }
02318
02319 for (;u != NULL && num_vehicles > 0; num_vehicles--, u = u->Next()) {
02320
02321 set.Include(u->index);
02322
02323
02324 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02325 }
02326
02327
02328 while (u != NULL && u->IsArticulatedPart()) {
02329 set.Include(u->index);
02330 u = u->Next();
02331 }
02332 }
02333 }