00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "error.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 "train.h"
00023 #include "aircraft.h"
00024 #include "newgrf_debug.h"
00025 #include "newgrf_sound.h"
00026 #include "newgrf_station.h"
00027 #include "group_gui.h"
00028 #include "strings_func.h"
00029 #include "zoom_func.h"
00030 #include "date_func.h"
00031 #include "vehicle_func.h"
00032 #include "autoreplace_func.h"
00033 #include "autoreplace_gui.h"
00034 #include "station_base.h"
00035 #include "ai/ai.hpp"
00036 #include "depot_func.h"
00037 #include "network/network.h"
00038 #include "core/pool_func.hpp"
00039 #include "economy_base.h"
00040 #include "articulated_vehicles.h"
00041 #include "roadstop_base.h"
00042 #include "core/random_func.hpp"
00043 #include "core/backup_type.hpp"
00044 #include "order_backup.h"
00045 #include "sound_func.h"
00046 #include "effectvehicle_func.h"
00047 #include "effectvehicle_base.h"
00048 #include "vehiclelist.h"
00049 #include "bridge_map.h"
00050 #include "tunnel_map.h"
00051 #include "depot_map.h"
00052 #include "gamelog.h"
00053
00054 #include "table/strings.h"
00055
00056 #define GEN_HASH(x, y) ((GB((y), 6 + ZOOM_LVL_SHIFT, 6) << 6) + GB((x), 7 + ZOOM_LVL_SHIFT, 6))
00057
00058 VehicleID _new_vehicle_id;
00059 uint16 _returned_refit_capacity;
00060 uint16 _returned_mail_refit_capacity;
00061
00062
00064 VehiclePool _vehicle_pool("Vehicle");
00065 INSTANTIATE_POOL_METHODS(Vehicle)
00066
00067
00072 bool Vehicle::NeedsAutorenewing(const Company *c) const
00073 {
00074
00075
00076
00077
00078 assert(c == Company::Get(this->owner));
00079
00080 if (!c->settings.engine_renew) return false;
00081 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00082
00083
00084 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
00085
00086 return true;
00087 }
00088
00089 void VehicleServiceInDepot(Vehicle *v)
00090 {
00091 v->date_of_last_service = _date;
00092 v->breakdowns_since_last_service = 0;
00093 v->reliability = v->GetEngine()->reliability;
00094 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00095 }
00096
00103 bool Vehicle::NeedsServicing() const
00104 {
00105
00106
00107 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00108
00109
00110 const Company *c = Company::Get(this->owner);
00111 if (c->settings.vehicle.servint_ispercent ?
00112 (this->reliability >= this->GetEngine()->reliability * (100 - this->service_interval) / 100) :
00113 (this->date_of_last_service + this->service_interval >= _date)) {
00114 return false;
00115 }
00116
00117
00118
00119 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00120 _settings_game.difficulty.vehicle_breakdowns != 0) {
00121 return true;
00122 }
00123
00124
00125
00126
00127 bool pending_replace = false;
00128 Money needed_money = c->settings.engine_renew_money;
00129 if (needed_money > c->money) return false;
00130
00131 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00132 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
00133
00134
00135 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00136
00137
00138 uint32 available_cargo_types, union_mask;
00139 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00140
00141 if (union_mask != 0) {
00142 CargoID cargo_type;
00143
00144 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
00145
00146
00147 if (cargo_type != CT_INVALID) {
00148
00149 if (!HasBit(available_cargo_types, cargo_type)) continue;
00150 }
00151 }
00152
00153
00154
00155 pending_replace = true;
00156 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00157 if (needed_money > c->money) return false;
00158 }
00159
00160 return pending_replace;
00161 }
00162
00168 bool Vehicle::NeedsAutomaticServicing() const
00169 {
00170 if (this->HasDepotOrder()) return false;
00171 if (this->current_order.IsType(OT_LOADING)) return false;
00172 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00173 return NeedsServicing();
00174 }
00175
00176 uint Vehicle::Crash(bool flooded)
00177 {
00178 assert((this->vehstatus & VS_CRASHED) == 0);
00179 assert(this->Previous() == NULL);
00180
00181 uint pass = 0;
00182
00183 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00184
00185 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00186 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00187 v->vehstatus |= VS_CRASHED;
00188 MarkSingleVehicleDirty(v);
00189 }
00190
00191
00192 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00193 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
00194 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00195 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00196
00197 delete this->cargo_payment;
00198 this->cargo_payment = NULL;
00199
00200 return pass;
00201 }
00202
00203
00212 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00213 {
00214 const Engine *e = Engine::Get(engine);
00215 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
00216
00217 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00218 SetBit(grfconfig->grf_bugs, bug_type);
00219 SetDParamStr(0, grfconfig->GetName());
00220 SetDParam(1, engine);
00221 ShowErrorMessage(part1, part2, WL_CRITICAL);
00222 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00223 }
00224
00225
00226 char buffer[512];
00227
00228 SetDParamStr(0, grfconfig->GetName());
00229 GetString(buffer, part1, lastof(buffer));
00230 DEBUG(grf, 0, "%s", buffer + 3);
00231
00232 SetDParam(1, engine);
00233 GetString(buffer, part2, lastof(buffer));
00234 DEBUG(grf, 0, "%s", buffer + 3);
00235 }
00236
00242 void VehicleLengthChanged(const Vehicle *u)
00243 {
00244
00245 const Engine *engine = u->GetEngine();
00246 uint32 grfid = engine->grf_prop.grffile->grfid;
00247 GRFConfig *grfconfig = GetGRFConfig(grfid);
00248 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
00249 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
00250 }
00251 }
00252
00257 Vehicle::Vehicle(VehicleType type)
00258 {
00259 this->type = type;
00260 this->coord.left = INVALID_COORD;
00261 this->group_id = DEFAULT_GROUP;
00262 this->fill_percent_te_id = INVALID_TE_ID;
00263 this->first = this;
00264 this->colourmap = PAL_NONE;
00265 this->cargo_age_counter = 1;
00266 }
00267
00272 byte VehicleRandomBits()
00273 {
00274 return GB(Random(), 0, 8);
00275 }
00276
00277
00278
00279 const int HASH_BITS = 7;
00280 const int HASH_SIZE = 1 << HASH_BITS;
00281 const int HASH_MASK = HASH_SIZE - 1;
00282 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00283 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00284
00285
00286
00287 const int HASH_RES = 0;
00288
00289 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
00290
00291 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00292 {
00293 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00294 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00295 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00296 for (; v != NULL; v = v->hash_tile_next) {
00297 Vehicle *a = proc(v, data);
00298 if (find_first && a != NULL) return a;
00299 }
00300 if (x == xu) break;
00301 }
00302 if (y == yu) break;
00303 }
00304
00305 return NULL;
00306 }
00307
00308
00320 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00321 {
00322 const int COLL_DIST = 6;
00323
00324
00325 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00326 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00327 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00328 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00329
00330 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
00331 }
00332
00347 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00348 {
00349 VehicleFromPosXY(x, y, data, proc, false);
00350 }
00351
00363 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00364 {
00365 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00366 }
00367
00378 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00379 {
00380 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00381 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00382
00383 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00384 for (; v != NULL; v = v->hash_tile_next) {
00385 if (v->tile != tile) continue;
00386
00387 Vehicle *a = proc(v, data);
00388 if (find_first && a != NULL) return a;
00389 }
00390
00391 return NULL;
00392 }
00393
00407 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00408 {
00409 VehicleFromPos(tile, data, proc, false);
00410 }
00411
00422 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00423 {
00424 return VehicleFromPos(tile, data, proc, true) != NULL;
00425 }
00426
00433 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00434 {
00435 int z = *(int*)data;
00436
00437 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00438 if (v->z_pos > z) return NULL;
00439
00440 return v;
00441 }
00442
00448 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00449 {
00450 int z = GetTileMaxPixelZ(tile);
00451
00452
00453
00454
00455
00456 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00457 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00458 return CommandCost();
00459 }
00460
00462 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00463 {
00464 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00465 if (v == (const Vehicle *)data) return NULL;
00466
00467 return v;
00468 }
00469
00477 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00478 {
00479
00480
00481
00482
00483 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00484 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00485
00486 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00487 return CommandCost();
00488 }
00489
00490 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00491 {
00492 TrackBits rail_bits = *(TrackBits *)data;
00493
00494 if (v->type != VEH_TRAIN) return NULL;
00495
00496 Train *t = Train::From(v);
00497 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00498
00499 return v;
00500 }
00501
00510 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00511 {
00512
00513
00514
00515
00516 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00517 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00518 return CommandCost();
00519 }
00520
00521 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
00522 {
00523 Vehicle **old_hash = v->hash_tile_current;
00524 Vehicle **new_hash;
00525
00526 if (remove) {
00527 new_hash = NULL;
00528 } else {
00529 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00530 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00531 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00532 }
00533
00534 if (old_hash == new_hash) return;
00535
00536
00537 if (old_hash != NULL) {
00538 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
00539 *v->hash_tile_prev = v->hash_tile_next;
00540 }
00541
00542
00543 if (new_hash != NULL) {
00544 v->hash_tile_next = *new_hash;
00545 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
00546 v->hash_tile_prev = new_hash;
00547 *new_hash = v;
00548 }
00549
00550
00551 v->hash_tile_current = new_hash;
00552 }
00553
00554 static Vehicle *_vehicle_viewport_hash[0x1000];
00555
00556 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
00557 {
00558 Vehicle **old_hash, **new_hash;
00559 int old_x = v->coord.left;
00560 int old_y = v->coord.top;
00561
00562 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
00563 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
00564
00565 if (old_hash == new_hash) return;
00566
00567
00568 if (old_hash != NULL) {
00569 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
00570 *v->hash_viewport_prev = v->hash_viewport_next;
00571 }
00572
00573
00574 if (new_hash != NULL) {
00575 v->hash_viewport_next = *new_hash;
00576 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
00577 v->hash_viewport_prev = new_hash;
00578 *new_hash = v;
00579 }
00580 }
00581
00582 void ResetVehicleHash()
00583 {
00584 Vehicle *v;
00585 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
00586 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
00587 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
00588 }
00589
00590 void ResetVehicleColourMap()
00591 {
00592 Vehicle *v;
00593 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00594 }
00595
00600 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00601 static AutoreplaceMap _vehicles_to_autoreplace;
00602
00603 void InitializeVehicles()
00604 {
00605 _vehicles_to_autoreplace.Reset();
00606 ResetVehicleHash();
00607 }
00608
00609 uint CountVehiclesInChain(const Vehicle *v)
00610 {
00611 uint count = 0;
00612 do count++; while ((v = v->Next()) != NULL);
00613 return count;
00614 }
00615
00620 bool Vehicle::IsEngineCountable() const
00621 {
00622 switch (this->type) {
00623 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00624 case VEH_TRAIN:
00625 return !this->IsArticulatedPart() &&
00626 !Train::From(this)->IsRearDualheaded();
00627 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00628 case VEH_SHIP: return true;
00629 default: return false;
00630 }
00631 }
00632
00637 bool Vehicle::HasEngineType() const
00638 {
00639 switch (this->type) {
00640 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00641 case VEH_TRAIN:
00642 case VEH_ROAD:
00643 case VEH_SHIP: return true;
00644 default: return false;
00645 }
00646 }
00647
00653 const Engine *Vehicle::GetEngine() const
00654 {
00655 return Engine::Get(this->engine_type);
00656 }
00657
00663 const GRFFile *Vehicle::GetGRF() const
00664 {
00665 return this->GetEngine()->GetGRF();
00666 }
00667
00673 uint32 Vehicle::GetGRFID() const
00674 {
00675 return this->GetEngine()->GetGRFID();
00676 }
00677
00685 void Vehicle::HandlePathfindingResult(bool path_found)
00686 {
00687 if (path_found) {
00688
00689 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00690
00691
00692 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00693
00694 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00695 return;
00696 }
00697
00698
00699 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00700
00701
00702 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00703
00704 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
00705 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00706 SetDParam(0, this->index);
00707 AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00708 }
00709 }
00710
00712 void Vehicle::PreDestructor()
00713 {
00714 if (CleaningPool()) return;
00715
00716 if (Station::IsValidID(this->last_station_visited)) {
00717 Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00718
00719 HideFillingPercent(&this->fill_percent_te_id);
00720
00721 delete this->cargo_payment;
00722 }
00723
00724 if (this->IsEngineCountable()) {
00725 GroupStatistics::CountEngine(this, -1);
00726 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
00727 GroupStatistics::UpdateAutoreplace(this->owner);
00728
00729 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00730 DeleteGroupHighlightOfVehicle(this);
00731 }
00732
00733 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00734 Aircraft *a = Aircraft::From(this);
00735 Station *st = GetTargetAirportIfValid(a);
00736 if (st != NULL) {
00737 const AirportFTA *layout = st->airport.GetFTA()->layout;
00738 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00739 }
00740 }
00741
00742
00743 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00744 RoadVehicle *v = RoadVehicle::From(this);
00745 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00746
00747 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00748 }
00749 }
00750
00751 if (this->Previous() == NULL) {
00752 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00753 }
00754
00755 if (this->IsPrimaryVehicle()) {
00756 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00757 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00758 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00759 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00760 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00761 SetWindowDirty(WC_COMPANY, this->owner);
00762 OrderBackup::ClearVehicle(this);
00763 }
00764 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00765
00766 this->cargo.Truncate(0);
00767 DeleteVehicleOrders(this);
00768 DeleteDepotHighlightOfVehicle(this);
00769
00770 extern void StopGlobalFollowVehicle(const Vehicle *v);
00771 StopGlobalFollowVehicle(this);
00772
00773 ReleaseDisastersTargetingVehicle(this->index);
00774 }
00775
00776 Vehicle::~Vehicle()
00777 {
00778 free(this->name);
00779
00780 if (CleaningPool()) {
00781 this->cargo.OnCleanPool();
00782 return;
00783 }
00784
00785
00786
00787 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00788
00789 Vehicle *v = this->Next();
00790 this->SetNext(NULL);
00791
00792 delete v;
00793
00794 UpdateVehicleTileHash(this, true);
00795 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
00796 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00797 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00798 }
00799
00804 void VehicleEnteredDepotThisTick(Vehicle *v)
00805 {
00806
00807 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00808
00809
00810
00811
00812
00813
00814 v->vehstatus |= VS_STOPPED;
00815 }
00816
00822 static void RunVehicleDayProc()
00823 {
00824 if (_game_mode != GM_NORMAL) return;
00825
00826
00827 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00828 Vehicle *v = Vehicle::Get(i);
00829 if (v == NULL) continue;
00830
00831
00832 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
00833 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00834 if (callback != CALLBACK_FAILED) {
00835 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00836 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00837
00838 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
00839 }
00840 }
00841
00842
00843 v->OnNewDay();
00844 }
00845 }
00846
00847 void CallVehicleTicks()
00848 {
00849 _vehicles_to_autoreplace.Clear();
00850
00851 RunVehicleDayProc();
00852
00853 Station *st;
00854 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00855
00856 Vehicle *v;
00857 FOR_ALL_VEHICLES(v) {
00858
00859 if (!v->Tick()) {
00860 assert(Vehicle::Get(vehicle_index) == NULL);
00861 continue;
00862 }
00863
00864 assert(Vehicle::Get(vehicle_index) == v);
00865
00866 switch (v->type) {
00867 default: break;
00868
00869 case VEH_TRAIN:
00870 case VEH_ROAD:
00871 case VEH_AIRCRAFT:
00872 case VEH_SHIP:
00873 if (v->vcache.cached_cargo_age_period != 0) {
00874 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
00875 if (--v->cargo_age_counter == 0) {
00876 v->cargo.AgeCargo();
00877 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
00878 }
00879 }
00880
00881 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00882 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00883 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00884
00885 v->motion_counter += v->cur_speed;
00886
00887 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00888
00889
00890 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00891 }
00892 }
00893
00894 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00895 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00896 v = it->first;
00897
00898 cur_company.Change(v->owner);
00899
00900
00901
00902
00903 if (it->second) v->vehstatus &= ~VS_STOPPED;
00904
00905
00906 int x = v->x_pos;
00907 int y = v->y_pos;
00908 int z = v->z_pos;
00909
00910 const Company *c = Company::Get(_current_company);
00911 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00912 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00913 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00914
00915 if (!IsLocalCompany()) continue;
00916
00917 if (res.Succeeded()) {
00918 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00919 continue;
00920 }
00921
00922 StringID error_message = res.GetErrorMessage();
00923 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00924
00925 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00926
00927 StringID message;
00928 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00929 message = error_message;
00930 } else {
00931 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00932 }
00933
00934 SetDParam(0, v->index);
00935 SetDParam(1, error_message);
00936 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00937 }
00938
00939 cur_company.Restore();
00940 }
00941
00946 static void DoDrawVehicle(const Vehicle *v)
00947 {
00948 SpriteID image = v->cur_image;
00949 PaletteID pal = PAL_NONE;
00950
00951 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00952
00953
00954 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
00955
00956 if (v->type == VEH_EFFECT) {
00957
00958
00959 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00960 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00961 }
00962
00963 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00964 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
00965 }
00966
00971 void ViewportAddVehicles(DrawPixelInfo *dpi)
00972 {
00973
00974 const int l = dpi->left;
00975 const int r = dpi->left + dpi->width;
00976 const int t = dpi->top;
00977 const int b = dpi->top + dpi->height;
00978
00979
00980 int xl, xu, yl, yu;
00981
00982 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
00983 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
00984 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
00985 } else {
00986
00987 xl = 0;
00988 xu = 0x3F;
00989 }
00990
00991 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
00992 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
00993 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
00994 } else {
00995
00996 yl = 0;
00997 yu = 0x3F << 6;
00998 }
00999
01000 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
01001 for (int x = xl;; x = (x + 1) & 0x3F) {
01002 const Vehicle *v = _vehicle_viewport_hash[x + y];
01003
01004 while (v != NULL) {
01005 if (!(v->vehstatus & VS_HIDDEN) &&
01006 l <= v->coord.right &&
01007 t <= v->coord.bottom &&
01008 r >= v->coord.left &&
01009 b >= v->coord.top) {
01010 DoDrawVehicle(v);
01011 }
01012 v = v->hash_viewport_next;
01013 }
01014
01015 if (x == xu) break;
01016 }
01017
01018 if (y == yu) break;
01019 }
01020 }
01021
01029 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01030 {
01031 Vehicle *found = NULL, *v;
01032 uint dist, best_dist = UINT_MAX;
01033
01034 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01035
01036 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01037 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01038
01039 FOR_ALL_VEHICLES(v) {
01040 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
01041 x >= v->coord.left && x <= v->coord.right &&
01042 y >= v->coord.top && y <= v->coord.bottom) {
01043
01044 dist = max(
01045 abs(((v->coord.left + v->coord.right) >> 1) - x),
01046 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01047 );
01048
01049 if (dist < best_dist) {
01050 found = v;
01051 best_dist = dist;
01052 }
01053 }
01054 }
01055
01056 return found;
01057 }
01058
01063 void DecreaseVehicleValue(Vehicle *v)
01064 {
01065 v->value -= v->value >> 8;
01066 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01067 }
01068
01069 static const byte _breakdown_chance[64] = {
01070 3, 3, 3, 3, 3, 3, 3, 3,
01071 4, 4, 5, 5, 6, 6, 7, 7,
01072 8, 8, 9, 9, 10, 10, 11, 11,
01073 12, 13, 13, 13, 13, 14, 15, 16,
01074 17, 19, 21, 25, 28, 31, 34, 37,
01075 40, 44, 48, 52, 56, 60, 64, 68,
01076 72, 80, 90, 100, 110, 120, 130, 140,
01077 150, 170, 190, 210, 230, 250, 250, 250,
01078 };
01079
01080 void CheckVehicleBreakdown(Vehicle *v)
01081 {
01082 int rel, rel_old;
01083
01084
01085 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01086 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01087
01088 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01089 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01090 v->cur_speed < 5 || _game_mode == GM_MENU) {
01091 return;
01092 }
01093
01094 uint32 r = Random();
01095
01096
01097 int chance = v->breakdown_chance + 1;
01098 if (Chance16I(1, 25, r)) chance += 25;
01099 v->breakdown_chance = min(255, chance);
01100
01101
01102 rel = v->reliability;
01103 if (v->type == VEH_SHIP) rel += 0x6666;
01104
01105
01106 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01107
01108
01109 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01110 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01111 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01112 v->breakdown_chance = 0;
01113 }
01114 }
01115
01122 bool Vehicle::HandleBreakdown()
01123 {
01124
01125
01126
01127
01128
01129 switch (this->breakdown_ctr) {
01130 case 0:
01131 return false;
01132
01133 case 2:
01134 this->breakdown_ctr = 1;
01135
01136 if (this->breakdowns_since_last_service != 255) {
01137 this->breakdowns_since_last_service++;
01138 }
01139
01140 if (this->type == VEH_AIRCRAFT) {
01141
01142 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01143 } else {
01144 this->cur_speed = 0;
01145
01146 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01147 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01148 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01149 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01150 }
01151
01152 if (!(this->vehstatus & VS_HIDDEN)) {
01153 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01154 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01155 }
01156 }
01157
01158 this->MarkDirty();
01159 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01160 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01161
01162
01163 case 1:
01164
01165 if (this->type == VEH_AIRCRAFT) return false;
01166
01167
01168 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01169 if (--this->breakdown_delay == 0) {
01170 this->breakdown_ctr = 0;
01171 this->MarkDirty();
01172 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01173 }
01174 }
01175 return true;
01176
01177 default:
01178 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01179 return false;
01180 }
01181 }
01182
01187 void AgeVehicle(Vehicle *v)
01188 {
01189 if (v->age < MAX_DAY) {
01190 v->age++;
01191 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
01192 }
01193
01194 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
01195
01196 int age = v->age - v->max_age;
01197 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01198 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01199 v->reliability_spd_dec <<= 1;
01200 }
01201
01202 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01203
01204
01205 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01206
01207
01208 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
01209
01210 StringID str;
01211 if (age == -DAYS_IN_LEAP_YEAR) {
01212 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01213 } else if (age == 0) {
01214 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01215 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01216 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01217 } else {
01218 return;
01219 }
01220
01221 SetDParam(0, v->index);
01222 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01223 }
01224
01231 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01232 {
01233 int count = 0;
01234 int max = 0;
01235 int cars = 0;
01236 int unloading = 0;
01237 bool loading = false;
01238
01239 const Vehicle *u = v;
01240
01241 const Station *st = Station::GetIfValid(v->last_station_visited);
01242 assert(colour == NULL || st != NULL);
01243
01244
01245 for (; v != NULL; v = v->Next()) {
01246 count += v->cargo.Count();
01247 max += v->cargo_cap;
01248 if (v->cargo_cap != 0 && colour != NULL) {
01249 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01250 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01251 cars++;
01252 }
01253 }
01254
01255 if (colour != NULL) {
01256 if (unloading == 0 && loading) {
01257 *colour = STR_PERCENT_UP;
01258 } else if (cars == unloading || !loading) {
01259 *colour = STR_PERCENT_DOWN;
01260 } else {
01261 *colour = STR_PERCENT_UP_DOWN;
01262 }
01263 }
01264
01265
01266 if (max == 0) return 100;
01267
01268
01269 return (count * 100) / max;
01270 }
01271
01276 void VehicleEnterDepot(Vehicle *v)
01277 {
01278
01279 assert(v == v->First());
01280
01281 switch (v->type) {
01282 case VEH_TRAIN: {
01283 Train *t = Train::From(v);
01284 SetWindowClassesDirty(WC_TRAINS_LIST);
01285
01286 SetDepotReservation(t->tile, false);
01287 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01288
01289 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01290 t->wait_counter = 0;
01291 t->force_proceed = TFP_NONE;
01292 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01293 t->ConsistChanged(true);
01294 break;
01295 }
01296
01297 case VEH_ROAD:
01298 SetWindowClassesDirty(WC_ROADVEH_LIST);
01299 break;
01300
01301 case VEH_SHIP: {
01302 SetWindowClassesDirty(WC_SHIPS_LIST);
01303 Ship *ship = Ship::From(v);
01304 ship->state = TRACK_BIT_DEPOT;
01305 ship->UpdateCache();
01306 ship->UpdateViewport(true, true);
01307 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01308 break;
01309 }
01310
01311 case VEH_AIRCRAFT:
01312 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01313 HandleAircraftEnterHangar(Aircraft::From(v));
01314 break;
01315 default: NOT_REACHED();
01316 }
01317 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01318
01319 if (v->type != VEH_TRAIN) {
01320
01321
01322 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01323 }
01324 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01325
01326 v->vehstatus |= VS_HIDDEN;
01327 v->cur_speed = 0;
01328
01329 VehicleServiceInDepot(v);
01330
01331 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01332
01333 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01334 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01335
01336 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01337 Order t = v->current_order;
01338 v->current_order.MakeDummy();
01339
01340
01341
01342 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01343 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01344 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01345
01346 return;
01347 }
01348
01349 if (t.IsRefit()) {
01350 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01351 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01352 cur_company.Restore();
01353
01354 if (cost.Failed()) {
01355 _vehicles_to_autoreplace[v] = false;
01356 if (v->owner == _local_company) {
01357
01358 SetDParam(0, v->index);
01359 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01360 }
01361 } else if (cost.GetCost() != 0) {
01362 v->profit_this_year -= cost.GetCost() << 8;
01363 if (v->owner == _local_company) {
01364 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01365 }
01366 }
01367 }
01368
01369 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01370
01371 v->DeleteUnreachedImplicitOrders();
01372 UpdateVehicleTimetable(v, true);
01373 v->IncrementImplicitOrderIndex();
01374 }
01375 if (t.GetDepotActionType() & ODATFB_HALT) {
01376
01377 _vehicles_to_autoreplace[v] = false;
01378 if (v->owner == _local_company) {
01379 SetDParam(0, v->index);
01380 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01381 }
01382 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01383 }
01384 }
01385 }
01386
01387
01393 void VehicleUpdatePosition(Vehicle *v)
01394 {
01395 UpdateVehicleTileHash(v, false);
01396 }
01397
01404 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01405 {
01406 int img = v->cur_image;
01407 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01408 const Sprite *spr = GetSprite(img, ST_NORMAL);
01409
01410 pt.x += spr->x_offs;
01411 pt.y += spr->y_offs;
01412
01413 UpdateVehicleViewportHash(v, pt.x, pt.y);
01414
01415 Rect old_coord = v->coord;
01416 v->coord.left = pt.x;
01417 v->coord.top = pt.y;
01418 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01419 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01420
01421 if (dirty) {
01422 if (old_coord.left == INVALID_COORD) {
01423 MarkSingleVehicleDirty(v);
01424 } else {
01425 MarkAllViewportsDirty(
01426 min(old_coord.left, v->coord.left),
01427 min(old_coord.top, v->coord.top),
01428 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01429 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01430 );
01431 }
01432 }
01433 }
01434
01439 void VehicleUpdatePositionAndViewport(Vehicle *v)
01440 {
01441 VehicleUpdatePosition(v);
01442 VehicleUpdateViewport(v, true);
01443 }
01444
01449 void MarkSingleVehicleDirty(const Vehicle *v)
01450 {
01451 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01452 }
01453
01459 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01460 {
01461 static const int8 _delta_coord[16] = {
01462 -1,-1,-1, 0, 1, 1, 1, 0,
01463 -1, 0, 1, 1, 1, 0,-1,-1,
01464 };
01465
01466 int x = v->x_pos + _delta_coord[v->direction];
01467 int y = v->y_pos + _delta_coord[v->direction + 8];
01468
01469 GetNewVehiclePosResult gp;
01470 gp.x = x;
01471 gp.y = y;
01472 gp.old_tile = v->tile;
01473 gp.new_tile = TileVirtXY(x, y);
01474 return gp;
01475 }
01476
01477 static const Direction _new_direction_table[] = {
01478 DIR_N, DIR_NW, DIR_W,
01479 DIR_NE, DIR_SE, DIR_SW,
01480 DIR_E, DIR_SE, DIR_S
01481 };
01482
01483 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01484 {
01485 int i = 0;
01486
01487 if (y >= v->y_pos) {
01488 if (y != v->y_pos) i += 3;
01489 i += 3;
01490 }
01491
01492 if (x >= v->x_pos) {
01493 if (x != v->x_pos) i++;
01494 i++;
01495 }
01496
01497 Direction dir = v->direction;
01498
01499 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01500 if (dirdiff == DIRDIFF_SAME) return dir;
01501 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01502 }
01503
01513 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01514 {
01515 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01516 }
01517
01525 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01526 {
01527
01528 const Vehicle *v;
01529 FOR_ALL_VEHICLES(v) {
01530 if (v->type == type && v->owner == owner) {
01531 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01532 }
01533 }
01534
01535 if (this->maxid == 0) return;
01536
01537
01538
01539
01540 this->cache = CallocT<bool>(this->maxid + 2);
01541
01542
01543 FOR_ALL_VEHICLES(v) {
01544 if (v->type == type && v->owner == owner) {
01545 this->cache[v->unitnumber] = true;
01546 }
01547 }
01548 }
01549
01551 UnitID FreeUnitIDGenerator::NextID()
01552 {
01553 if (this->maxid <= this->curid) return ++this->curid;
01554
01555 while (this->cache[++this->curid]) { }
01556
01557 return this->curid;
01558 }
01559
01565 UnitID GetFreeUnitNumber(VehicleType type)
01566 {
01567
01568 uint max_veh;
01569 switch (type) {
01570 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01571 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01572 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01573 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01574 default: NOT_REACHED();
01575 }
01576
01577 const Company *c = Company::Get(_current_company);
01578 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01579
01580 FreeUnitIDGenerator gen(type, _current_company);
01581
01582 return gen.NextID();
01583 }
01584
01585
01594 bool CanBuildVehicleInfrastructure(VehicleType type)
01595 {
01596 assert(IsCompanyBuildableVehicleType(type));
01597
01598 if (!Company::IsValidID(_local_company)) return false;
01599 if (!_settings_client.gui.disable_unsuitable_building) return true;
01600
01601 UnitID max;
01602 switch (type) {
01603 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01604 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01605 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01606 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01607 default: NOT_REACHED();
01608 }
01609
01610
01611 if (max > 0) {
01612
01613 const Engine *e;
01614 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01615 if (HasBit(e->company_avail, _local_company)) return true;
01616 }
01617 return false;
01618 }
01619
01620
01621 const Vehicle *v;
01622 FOR_ALL_VEHICLES(v) {
01623 if (v->owner == _local_company && v->type == type) return true;
01624 }
01625
01626 return false;
01627 }
01628
01629
01637 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01638 {
01639 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01640 const Engine *e = Engine::Get(engine_type);
01641 switch (e->type) {
01642 default: NOT_REACHED();
01643 case VEH_TRAIN:
01644 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01645
01646
01647 engine_type = parent_engine_type;
01648 e = Engine::Get(engine_type);
01649
01650 }
01651
01652 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01653 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01654 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01655 if (!CargoSpec::Get(cargo_type)->is_freight) {
01656 if (parent_engine_type == INVALID_ENGINE) {
01657 return LS_PASSENGER_WAGON_STEAM;
01658 } else {
01659 switch (RailVehInfo(parent_engine_type)->engclass) {
01660 default: NOT_REACHED();
01661 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01662 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01663 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01664 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01665 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01666 }
01667 }
01668 } else {
01669 return LS_FREIGHT_WAGON;
01670 }
01671 } else {
01672 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01673
01674 switch (e->u.rail.engclass) {
01675 default: NOT_REACHED();
01676 case EC_STEAM: return LS_STEAM;
01677 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01678 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01679 case EC_MONORAIL: return LS_MONORAIL;
01680 case EC_MAGLEV: return LS_MAGLEV;
01681 }
01682 }
01683
01684 case VEH_ROAD:
01685
01686 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01687 engine_type = parent_engine_type;
01688 e = Engine::Get(engine_type);
01689 cargo_type = v->First()->cargo_type;
01690 }
01691 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01692 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01693
01694
01695 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01696
01697 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01698 } else {
01699
01700 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01701 }
01702
01703 case VEH_SHIP:
01704 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01705 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01706 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01707
01708 case VEH_AIRCRAFT:
01709 switch (e->u.air.subtype) {
01710 case AIR_HELI: return LS_HELICOPTER;
01711 case AIR_CTOL: return LS_SMALL_PLANE;
01712 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01713 default: NOT_REACHED();
01714 }
01715 }
01716 }
01717
01727 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01728 {
01729 const Company *c = Company::Get(company);
01730 LiveryScheme scheme = LS_DEFAULT;
01731
01732
01733
01734 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01735
01736 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01737
01738
01739 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01740 }
01741
01742 return &c->livery[scheme];
01743 }
01744
01745
01746 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01747 {
01748 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01749
01750
01751 if (map != PAL_NONE) return map;
01752
01753 const Engine *e = Engine::Get(engine_type);
01754
01755
01756 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01757 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01758
01759 if (callback != CALLBACK_FAILED) {
01760 assert_compile(PAL_NONE == 0);
01761 map = GB(callback, 0, 14);
01762
01763
01764 if (!HasBit(callback, 14)) {
01765
01766 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01767 return map;
01768 }
01769 }
01770 }
01771
01772 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01773
01774 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01775
01776
01777 if (!Company::IsValidID(company)) return map;
01778
01779 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01780
01781 map += livery->colour1;
01782 if (twocc) map += livery->colour2 * 16;
01783
01784
01785 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01786 return map;
01787 }
01788
01795 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01796 {
01797 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01798 }
01799
01805 PaletteID GetVehiclePalette(const Vehicle *v)
01806 {
01807 if (v->IsGroundVehicle()) {
01808 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01809 }
01810
01811 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01812 }
01813
01817 void Vehicle::DeleteUnreachedImplicitOrders()
01818 {
01819 if (this->IsGroundVehicle()) {
01820 uint16 &gv_flags = this->GetGroundVehicleFlags();
01821 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01822
01823 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01824 this->cur_implicit_order_index = this->cur_real_order_index;
01825 InvalidateVehicleOrder(this, 0);
01826 return;
01827 }
01828 }
01829
01830 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01831 while (order != NULL) {
01832 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01833
01834 if (order->IsType(OT_IMPLICIT)) {
01835
01836 order = order->next;
01837 DeleteOrder(this, this->cur_implicit_order_index);
01838 } else {
01839
01840 order = order->next;
01841 this->cur_implicit_order_index++;
01842 }
01843
01844
01845 if (order == NULL) {
01846 order = this->GetOrder(0);
01847 this->cur_implicit_order_index = 0;
01848 }
01849 }
01850 }
01851
01856 void Vehicle::BeginLoading()
01857 {
01858 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01859
01860 if (this->current_order.IsType(OT_GOTO_STATION) &&
01861 this->current_order.GetDestination() == this->last_station_visited) {
01862 this->DeleteUnreachedImplicitOrders();
01863
01864
01865 this->current_order.MakeLoading(true);
01866 UpdateVehicleTimetable(this, true);
01867
01868
01869
01870
01871
01872
01873 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01874
01875 } else {
01876
01877
01878
01879
01880
01881 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01882 if (this->IsGroundVehicle() && in_list != NULL &&
01883 (!in_list->IsType(OT_IMPLICIT) ||
01884 in_list->GetDestination() != this->last_station_visited)) {
01885 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01886
01887 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01888 if (prev_order == NULL ||
01889 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01890 prev_order->GetDestination() != this->last_station_visited) {
01891
01892
01893
01894 int target_index = this->cur_implicit_order_index;
01895 bool found = false;
01896 while (target_index != this->cur_real_order_index) {
01897 const Order *order = this->GetOrder(target_index);
01898 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01899 found = true;
01900 break;
01901 }
01902 target_index++;
01903 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01904 assert(target_index != this->cur_implicit_order_index);
01905 }
01906
01907 if (found) {
01908 if (suppress_implicit_orders) {
01909
01910 this->cur_implicit_order_index = target_index;
01911 InvalidateVehicleOrder(this, 0);
01912 } else {
01913
01914 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01915 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01916 if (order->IsType(OT_IMPLICIT)) {
01917
01918 order = order->next;
01919 DeleteOrder(this, this->cur_implicit_order_index);
01920 } else {
01921
01922 order = order->next;
01923 this->cur_implicit_order_index++;
01924 }
01925
01926
01927 if (order == NULL) {
01928 order = this->GetOrder(0);
01929 this->cur_implicit_order_index = 0;
01930 }
01931 assert(order != NULL);
01932 }
01933 }
01934 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01935
01936 Order *implicit_order = new Order();
01937 implicit_order->MakeImplicit(this->last_station_visited);
01938 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01939 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01940
01941
01942
01943 uint16 &gv_flags = this->GetGroundVehicleFlags();
01944 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01945 }
01946 }
01947 }
01948 this->current_order.MakeLoading(false);
01949 }
01950
01951 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01952
01953 PrepareUnload(this);
01954
01955 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01956 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
01957 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01958 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01959
01960 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01961 this->cur_speed = 0;
01962 this->MarkDirty();
01963 }
01964
01969 void Vehicle::LeaveStation()
01970 {
01971 assert(this->current_order.IsType(OT_LOADING));
01972
01973 delete this->cargo_payment;
01974
01975
01976 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01977
01978 this->current_order.MakeLeaveStation();
01979 Station *st = Station::Get(this->last_station_visited);
01980 st->loading_vehicles.remove(this);
01981
01982 HideFillingPercent(&this->fill_percent_te_id);
01983
01984 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01985
01986 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
01987
01988 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
01989 }
01990 }
01991
01992
01998 void Vehicle::HandleLoading(bool mode)
01999 {
02000 switch (this->current_order.GetType()) {
02001 case OT_LOADING: {
02002 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02003
02004
02005 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02006
02007 this->PlayLeaveStationSound();
02008
02009 this->LeaveStation();
02010
02011
02012 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02013 if (order == NULL ||
02014 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02015 order->GetDestination() != this->last_station_visited) {
02016 return;
02017 }
02018 break;
02019 }
02020
02021 case OT_DUMMY: break;
02022
02023 default: return;
02024 }
02025
02026 this->IncrementImplicitOrderIndex();
02027 }
02028
02035 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02036 {
02037 CommandCost ret = CheckOwnership(this->owner);
02038 if (ret.Failed()) return ret;
02039
02040 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02041 if (this->IsStoppedInDepot()) return CMD_ERROR;
02042
02043 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02044 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02045 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02046
02047
02048
02049 if (flags & DC_EXEC) {
02050 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02051 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02052 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02053 }
02054 return CommandCost();
02055 }
02056
02057 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02058 if (flags & DC_EXEC) {
02059
02060
02061 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02062
02063 if (this->IsGroundVehicle()) {
02064 uint16 &gv_flags = this->GetGroundVehicleFlags();
02065 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02066 }
02067
02068 this->current_order.MakeDummy();
02069 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02070 }
02071 return CommandCost();
02072 }
02073
02074 TileIndex location;
02075 DestinationID destination;
02076 bool reverse;
02077 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};
02078 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02079
02080 if (flags & DC_EXEC) {
02081 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02082
02083 if (this->IsGroundVehicle()) {
02084 uint16 &gv_flags = this->GetGroundVehicleFlags();
02085 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02086 }
02087
02088 this->dest_tile = location;
02089 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02090 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02091 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02092
02093
02094 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02095
02096 if (this->type == VEH_AIRCRAFT) {
02097 Aircraft *a = Aircraft::From(this);
02098 if (a->state == FLYING && a->targetairport != destination) {
02099
02100 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02101 AircraftNextAirportPos_and_Order(a);
02102 }
02103 }
02104 }
02105
02106 return CommandCost();
02107
02108 }
02109
02114 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02115 {
02116 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02117 const Engine *e = this->GetEngine();
02118
02119
02120 byte visual_effect;
02121 switch (e->type) {
02122 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02123 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02124 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02125 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02126 }
02127
02128
02129 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02130 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02131
02132 if (callback != CALLBACK_FAILED) {
02133 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02134
02135 callback = GB(callback, 0, 8);
02136
02137
02138 if (callback == VE_DEFAULT) {
02139 assert(HasBit(callback, VE_DISABLE_EFFECT));
02140 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02141 }
02142 visual_effect = callback;
02143 }
02144 }
02145
02146
02147 if (visual_effect == VE_DEFAULT ||
02148 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02149
02150
02151 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02152 if (visual_effect == VE_DEFAULT) {
02153 visual_effect = 1 << VE_DISABLE_EFFECT;
02154 } else {
02155 SetBit(visual_effect, VE_DISABLE_EFFECT);
02156 }
02157 } else {
02158 if (visual_effect == VE_DEFAULT) {
02159
02160 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02161 }
02162 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02163 }
02164 }
02165
02166 this->vcache.cached_vis_effect = visual_effect;
02167
02168 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02169 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02170 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02171 }
02172 }
02173
02174 static const int8 _vehicle_smoke_pos[8] = {
02175 1, 1, 1, 0, -1, -1, -1, 0
02176 };
02177
02182 void Vehicle::ShowVisualEffect() const
02183 {
02184 assert(this->IsPrimaryVehicle());
02185 bool sound = false;
02186
02187
02188
02189
02190
02191
02192 if (_settings_game.vehicle.smoke_amount == 0 ||
02193 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02194 this->cur_speed < 2) {
02195 return;
02196 }
02197
02198 uint max_speed = this->vcache.cached_max_speed;
02199 if (this->type == VEH_TRAIN) {
02200 const Train *t = Train::From(this);
02201
02202
02203
02204
02205 if (HasBit(t->flags, VRF_REVERSING) ||
02206 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02207 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02208 return;
02209 }
02210
02211 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02212 max_speed = min(max_speed, this->current_order.max_speed);
02213 }
02214 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02215
02216 const Vehicle *v = this;
02217
02218 do {
02219 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02220 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02221 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02222
02223
02224
02225
02226
02227
02228
02229
02230 if (disable_effect ||
02231 v->vehstatus & VS_HIDDEN ||
02232 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02233 IsDepotTile(v->tile) ||
02234 IsTunnelTile(v->tile) ||
02235 (v->type == VEH_TRAIN &&
02236 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02237 continue;
02238 }
02239
02240
02241
02242
02243 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02244
02245 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02246 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02247
02248 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02249 x = -x;
02250 y = -y;
02251 }
02252
02253 switch (effect_type) {
02254 case VE_TYPE_STEAM:
02255
02256
02257
02258
02259
02260 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02261 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02262 sound = true;
02263 }
02264 break;
02265
02266 case VE_TYPE_DIESEL: {
02267
02268
02269
02270
02271
02272
02273
02274
02275
02276
02277
02278 int power_weight_effect = 0;
02279 if (v->type == VEH_TRAIN) {
02280 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02281 }
02282 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02283 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02284 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02285 sound = true;
02286 }
02287 break;
02288 }
02289
02290 case VE_TYPE_ELECTRIC:
02291
02292
02293
02294
02295
02296
02297 if (GB(v->tick_counter, 0, 2) == 0 &&
02298 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02299 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02300 sound = true;
02301 }
02302 break;
02303
02304 default:
02305 break;
02306 }
02307 } while ((v = v->Next()) != NULL);
02308
02309 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02310 }
02311
02316 void Vehicle::SetNext(Vehicle *next)
02317 {
02318 assert(this != next);
02319
02320 if (this->next != NULL) {
02321
02322 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02323 v->first = this->next;
02324 }
02325 this->next->previous = NULL;
02326 }
02327
02328 this->next = next;
02329
02330 if (this->next != NULL) {
02331
02332 if (this->next->previous != NULL) this->next->previous->next = NULL;
02333 this->next->previous = this;
02334 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02335 v->first = this->first;
02336 }
02337 }
02338 }
02339
02345 void Vehicle::AddToShared(Vehicle *shared_chain)
02346 {
02347 assert(this->previous_shared == NULL && this->next_shared == NULL);
02348
02349 if (shared_chain->orders.list == NULL) {
02350 assert(shared_chain->previous_shared == NULL);
02351 assert(shared_chain->next_shared == NULL);
02352 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02353 }
02354
02355 this->next_shared = shared_chain->next_shared;
02356 this->previous_shared = shared_chain;
02357
02358 shared_chain->next_shared = this;
02359
02360 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02361
02362 shared_chain->orders.list->AddVehicle(this);
02363 }
02364
02368 void Vehicle::RemoveFromShared()
02369 {
02370
02371
02372 bool were_first = (this->FirstShared() == this);
02373 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02374
02375 this->orders.list->RemoveVehicle(this);
02376
02377 if (!were_first) {
02378
02379 this->previous_shared->next_shared = this->NextShared();
02380 }
02381
02382 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02383
02384
02385 if (this->orders.list->GetNumVehicles() == 1) {
02386
02387 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02388 InvalidateVehicleOrder(this->FirstShared(), 0);
02389 } else if (were_first) {
02390
02391
02392 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02393 }
02394
02395 this->next_shared = NULL;
02396 this->previous_shared = NULL;
02397 }
02398
02399 void VehiclesYearlyLoop()
02400 {
02401 Vehicle *v;
02402 FOR_ALL_VEHICLES(v) {
02403 if (v->IsPrimaryVehicle()) {
02404
02405 Money profit = v->GetDisplayProfitThisYear();
02406 if (v->age >= 730 && profit < 0) {
02407 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02408 SetDParam(0, v->index);
02409 SetDParam(1, profit);
02410 AddVehicleNewsItem(
02411 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02412 NS_ADVICE,
02413 v->index
02414 );
02415 }
02416 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02417 }
02418
02419 v->profit_last_year = v->profit_this_year;
02420 v->profit_this_year = 0;
02421 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02422 }
02423 }
02424 GroupStatistics::UpdateProfits();
02425 SetWindowClassesDirty(WC_TRAINS_LIST);
02426 SetWindowClassesDirty(WC_SHIPS_LIST);
02427 SetWindowClassesDirty(WC_ROADVEH_LIST);
02428 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02429 }
02430
02431
02441 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02442 {
02443 const Engine *e = Engine::GetIfValid(engine_type);
02444 assert(e != NULL);
02445
02446 switch (e->type) {
02447 case VEH_TRAIN:
02448 return (st->facilities & FACIL_TRAIN) != 0;
02449
02450 case VEH_ROAD:
02451
02452
02453
02454 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02455
02456 case VEH_SHIP:
02457 return (st->facilities & FACIL_DOCK) != 0;
02458
02459 case VEH_AIRCRAFT:
02460 return (st->facilities & FACIL_AIRPORT) != 0 &&
02461 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02462
02463 default:
02464 return false;
02465 }
02466 }
02467
02474 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02475 {
02476 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02477
02478 return CanVehicleUseStation(v->engine_type, st);
02479 }
02480
02486 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02487 {
02488 assert(this->IsGroundVehicle());
02489 if (this->type == VEH_TRAIN) {
02490 return &Train::From(this)->gcache;
02491 } else {
02492 return &RoadVehicle::From(this)->gcache;
02493 }
02494 }
02495
02501 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02502 {
02503 assert(this->IsGroundVehicle());
02504 if (this->type == VEH_TRAIN) {
02505 return &Train::From(this)->gcache;
02506 } else {
02507 return &RoadVehicle::From(this)->gcache;
02508 }
02509 }
02510
02516 uint16 &Vehicle::GetGroundVehicleFlags()
02517 {
02518 assert(this->IsGroundVehicle());
02519 if (this->type == VEH_TRAIN) {
02520 return Train::From(this)->gv_flags;
02521 } else {
02522 return RoadVehicle::From(this)->gv_flags;
02523 }
02524 }
02525
02531 const uint16 &Vehicle::GetGroundVehicleFlags() const
02532 {
02533 assert(this->IsGroundVehicle());
02534 if (this->type == VEH_TRAIN) {
02535 return Train::From(this)->gv_flags;
02536 } else {
02537 return RoadVehicle::From(this)->gv_flags;
02538 }
02539 }
02540
02549 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02550 {
02551 if (v->type == VEH_TRAIN) {
02552 Train *u = Train::From(v);
02553
02554 u = u->GetFirstEnginePart();
02555
02556
02557 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02558 do {
02559
02560 set.Include(u->index);
02561
02562
02563 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02564
02565 u = u->Next();
02566 } while (u != NULL && u->IsArticulatedPart());
02567 }
02568 }
02569 }