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
00073 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
00074 {
00075
00076
00077
00078
00079 assert(c == Company::Get(this->owner));
00080
00081 if (use_renew_setting && !c->settings.engine_renew) return false;
00082 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00083
00084
00085 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
00086
00087 return true;
00088 }
00089
00095 void VehicleServiceInDepot(Vehicle *v)
00096 {
00097 assert(v != NULL);
00098 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00099
00100 do {
00101 v->date_of_last_service = _date;
00102 v->breakdowns_since_last_service = 0;
00103 v->reliability = v->GetEngine()->reliability;
00104
00105 v->breakdown_chance /= 4;
00106 v = v->Next();
00107 } while (v != NULL && v->HasEngineType());
00108 }
00109
00116 bool Vehicle::NeedsServicing() const
00117 {
00118
00119
00120 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00121
00122
00123 const Company *c = Company::Get(this->owner);
00124 if (this->ServiceIntervalIsPercent() ?
00125 (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
00126 (this->date_of_last_service + this->GetServiceInterval() >= _date)) {
00127 return false;
00128 }
00129
00130
00131
00132 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00133 _settings_game.difficulty.vehicle_breakdowns != 0) {
00134 return true;
00135 }
00136
00137
00138
00139
00140 bool pending_replace = false;
00141 Money needed_money = c->settings.engine_renew_money;
00142 if (needed_money > c->money) return false;
00143
00144 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00145 bool replace_when_old = false;
00146 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
00147
00148
00149 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00150
00151 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
00152
00153
00154 uint32 available_cargo_types, union_mask;
00155 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00156
00157 if (union_mask != 0) {
00158 CargoID cargo_type;
00159
00160 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
00161
00162
00163 if (cargo_type != CT_INVALID) {
00164
00165 if (!HasBit(available_cargo_types, cargo_type)) continue;
00166 }
00167 }
00168
00169
00170
00171 pending_replace = true;
00172 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00173 if (needed_money > c->money) return false;
00174 }
00175
00176 return pending_replace;
00177 }
00178
00184 bool Vehicle::NeedsAutomaticServicing() const
00185 {
00186 if (this->HasDepotOrder()) return false;
00187 if (this->current_order.IsType(OT_LOADING)) return false;
00188 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00189 return NeedsServicing();
00190 }
00191
00192 uint Vehicle::Crash(bool flooded)
00193 {
00194 assert((this->vehstatus & VS_CRASHED) == 0);
00195 assert(this->Previous() == NULL);
00196
00197 uint pass = 0;
00198
00199 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00200
00201 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00202 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00203 v->vehstatus |= VS_CRASHED;
00204 MarkSingleVehicleDirty(v);
00205 }
00206
00207
00208 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00209 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
00210 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00211 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00212
00213 delete this->cargo_payment;
00214 this->cargo_payment = NULL;
00215
00216 return RandomRange(pass + 1);
00217 }
00218
00219
00228 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00229 {
00230 const Engine *e = Engine::Get(engine);
00231 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
00232
00233 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00234 SetBit(grfconfig->grf_bugs, bug_type);
00235 SetDParamStr(0, grfconfig->GetName());
00236 SetDParam(1, engine);
00237 ShowErrorMessage(part1, part2, WL_CRITICAL);
00238 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00239 }
00240
00241
00242 char buffer[512];
00243
00244 SetDParamStr(0, grfconfig->GetName());
00245 GetString(buffer, part1, lastof(buffer));
00246 DEBUG(grf, 0, "%s", buffer + 3);
00247
00248 SetDParam(1, engine);
00249 GetString(buffer, part2, lastof(buffer));
00250 DEBUG(grf, 0, "%s", buffer + 3);
00251 }
00252
00258 void VehicleLengthChanged(const Vehicle *u)
00259 {
00260
00261 const Engine *engine = u->GetEngine();
00262 uint32 grfid = engine->grf_prop.grffile->grfid;
00263 GRFConfig *grfconfig = GetGRFConfig(grfid);
00264 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
00265 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
00266 }
00267 }
00268
00273 Vehicle::Vehicle(VehicleType type)
00274 {
00275 this->type = type;
00276 this->coord.left = INVALID_COORD;
00277 this->group_id = DEFAULT_GROUP;
00278 this->fill_percent_te_id = INVALID_TE_ID;
00279 this->first = this;
00280 this->colourmap = PAL_NONE;
00281 this->cargo_age_counter = 1;
00282 }
00283
00288 byte VehicleRandomBits()
00289 {
00290 return GB(Random(), 0, 8);
00291 }
00292
00293
00294
00295 const int HASH_BITS = 7;
00296 const int HASH_SIZE = 1 << HASH_BITS;
00297 const int HASH_MASK = HASH_SIZE - 1;
00298 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00299 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00300
00301
00302
00303 const int HASH_RES = 0;
00304
00305 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
00306
00307 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00308 {
00309 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00310 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00311 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00312 for (; v != NULL; v = v->hash_tile_next) {
00313 Vehicle *a = proc(v, data);
00314 if (find_first && a != NULL) return a;
00315 }
00316 if (x == xu) break;
00317 }
00318 if (y == yu) break;
00319 }
00320
00321 return NULL;
00322 }
00323
00324
00336 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00337 {
00338 const int COLL_DIST = 6;
00339
00340
00341 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00342 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00343 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00344 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00345
00346 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
00347 }
00348
00363 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00364 {
00365 VehicleFromPosXY(x, y, data, proc, false);
00366 }
00367
00379 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00380 {
00381 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00382 }
00383
00394 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00395 {
00396 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00397 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00398
00399 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00400 for (; v != NULL; v = v->hash_tile_next) {
00401 if (v->tile != tile) continue;
00402
00403 Vehicle *a = proc(v, data);
00404 if (find_first && a != NULL) return a;
00405 }
00406
00407 return NULL;
00408 }
00409
00423 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00424 {
00425 VehicleFromPos(tile, data, proc, false);
00426 }
00427
00438 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00439 {
00440 return VehicleFromPos(tile, data, proc, true) != NULL;
00441 }
00442
00449 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00450 {
00451 int z = *(int*)data;
00452
00453 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00454 if (v->z_pos > z) return NULL;
00455
00456 return v;
00457 }
00458
00464 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00465 {
00466 int z = GetTileMaxPixelZ(tile);
00467
00468
00469
00470
00471
00472 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00473 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00474 return CommandCost();
00475 }
00476
00478 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00479 {
00480 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00481 if (v == (const Vehicle *)data) return NULL;
00482
00483 return v;
00484 }
00485
00493 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00494 {
00495
00496
00497
00498
00499 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00500 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00501
00502 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00503 return CommandCost();
00504 }
00505
00506 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00507 {
00508 TrackBits rail_bits = *(TrackBits *)data;
00509
00510 if (v->type != VEH_TRAIN) return NULL;
00511
00512 Train *t = Train::From(v);
00513 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00514
00515 return v;
00516 }
00517
00526 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00527 {
00528
00529
00530
00531
00532 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00533 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00534 return CommandCost();
00535 }
00536
00537 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
00538 {
00539 Vehicle **old_hash = v->hash_tile_current;
00540 Vehicle **new_hash;
00541
00542 if (remove) {
00543 new_hash = NULL;
00544 } else {
00545 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00546 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00547 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00548 }
00549
00550 if (old_hash == new_hash) return;
00551
00552
00553 if (old_hash != NULL) {
00554 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
00555 *v->hash_tile_prev = v->hash_tile_next;
00556 }
00557
00558
00559 if (new_hash != NULL) {
00560 v->hash_tile_next = *new_hash;
00561 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
00562 v->hash_tile_prev = new_hash;
00563 *new_hash = v;
00564 }
00565
00566
00567 v->hash_tile_current = new_hash;
00568 }
00569
00570 static Vehicle *_vehicle_viewport_hash[0x1000];
00571
00572 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
00573 {
00574 Vehicle **old_hash, **new_hash;
00575 int old_x = v->coord.left;
00576 int old_y = v->coord.top;
00577
00578 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
00579 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
00580
00581 if (old_hash == new_hash) return;
00582
00583
00584 if (old_hash != NULL) {
00585 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
00586 *v->hash_viewport_prev = v->hash_viewport_next;
00587 }
00588
00589
00590 if (new_hash != NULL) {
00591 v->hash_viewport_next = *new_hash;
00592 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
00593 v->hash_viewport_prev = new_hash;
00594 *new_hash = v;
00595 }
00596 }
00597
00598 void ResetVehicleHash()
00599 {
00600 Vehicle *v;
00601 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
00602 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
00603 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
00604 }
00605
00606 void ResetVehicleColourMap()
00607 {
00608 Vehicle *v;
00609 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00610 }
00611
00616 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00617 static AutoreplaceMap _vehicles_to_autoreplace;
00618
00619 void InitializeVehicles()
00620 {
00621 _vehicles_to_autoreplace.Reset();
00622 ResetVehicleHash();
00623 }
00624
00625 uint CountVehiclesInChain(const Vehicle *v)
00626 {
00627 uint count = 0;
00628 do count++; while ((v = v->Next()) != NULL);
00629 return count;
00630 }
00631
00636 bool Vehicle::IsEngineCountable() const
00637 {
00638 switch (this->type) {
00639 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00640 case VEH_TRAIN:
00641 return !this->IsArticulatedPart() &&
00642 !Train::From(this)->IsRearDualheaded();
00643 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00644 case VEH_SHIP: return true;
00645 default: return false;
00646 }
00647 }
00648
00653 bool Vehicle::HasEngineType() const
00654 {
00655 switch (this->type) {
00656 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00657 case VEH_TRAIN:
00658 case VEH_ROAD:
00659 case VEH_SHIP: return true;
00660 default: return false;
00661 }
00662 }
00663
00669 const Engine *Vehicle::GetEngine() const
00670 {
00671 return Engine::Get(this->engine_type);
00672 }
00673
00679 const GRFFile *Vehicle::GetGRF() const
00680 {
00681 return this->GetEngine()->GetGRF();
00682 }
00683
00689 uint32 Vehicle::GetGRFID() const
00690 {
00691 return this->GetEngine()->GetGRFID();
00692 }
00693
00701 void Vehicle::HandlePathfindingResult(bool path_found)
00702 {
00703 if (path_found) {
00704
00705 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00706
00707
00708 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00709
00710 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00711 return;
00712 }
00713
00714
00715 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00716
00717
00718 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00719
00720 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
00721 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00722 SetDParam(0, this->index);
00723 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
00724 }
00725 }
00726
00728 void Vehicle::PreDestructor()
00729 {
00730 if (CleaningPool()) return;
00731
00732 if (Station::IsValidID(this->last_station_visited)) {
00733 Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00734
00735 HideFillingPercent(&this->fill_percent_te_id);
00736
00737 delete this->cargo_payment;
00738 }
00739
00740 if (this->IsEngineCountable()) {
00741 GroupStatistics::CountEngine(this, -1);
00742 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
00743 GroupStatistics::UpdateAutoreplace(this->owner);
00744
00745 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00746 DeleteGroupHighlightOfVehicle(this);
00747 }
00748
00749 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00750 Aircraft *a = Aircraft::From(this);
00751 Station *st = GetTargetAirportIfValid(a);
00752 if (st != NULL) {
00753 const AirportFTA *layout = st->airport.GetFTA()->layout;
00754 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00755 }
00756 }
00757
00758
00759 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00760 RoadVehicle *v = RoadVehicle::From(this);
00761 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00762
00763 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00764 }
00765 }
00766
00767 if (this->Previous() == NULL) {
00768 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00769 }
00770
00771 if (this->IsPrimaryVehicle()) {
00772 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00773 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00774 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00775 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00776 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00777 SetWindowDirty(WC_COMPANY, this->owner);
00778 OrderBackup::ClearVehicle(this);
00779 }
00780 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00781
00782 this->cargo.Truncate(0);
00783 DeleteVehicleOrders(this);
00784 DeleteDepotHighlightOfVehicle(this);
00785
00786 extern void StopGlobalFollowVehicle(const Vehicle *v);
00787 StopGlobalFollowVehicle(this);
00788
00789 ReleaseDisastersTargetingVehicle(this->index);
00790 }
00791
00792 Vehicle::~Vehicle()
00793 {
00794 if (CleaningPool()) {
00795 this->cargo.OnCleanPool();
00796 return;
00797 }
00798
00799
00800
00801 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00802
00803 Vehicle *v = this->Next();
00804 this->SetNext(NULL);
00805
00806 delete v;
00807
00808 UpdateVehicleTileHash(this, true);
00809 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
00810 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00811 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00812 }
00813
00818 void VehicleEnteredDepotThisTick(Vehicle *v)
00819 {
00820
00821 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00822
00823
00824
00825
00826
00827
00828 v->vehstatus |= VS_STOPPED;
00829 }
00830
00836 static void RunVehicleDayProc()
00837 {
00838 if (_game_mode != GM_NORMAL) return;
00839
00840
00841 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00842 Vehicle *v = Vehicle::Get(i);
00843 if (v == NULL) continue;
00844
00845
00846 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
00847 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00848 if (callback != CALLBACK_FAILED) {
00849 if (HasBit(callback, 0)) {
00850
00851 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00852 v->First()->MarkDirty();
00853 }
00854 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00855
00856 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
00857 }
00858 }
00859
00860
00861 v->OnNewDay();
00862 }
00863 }
00864
00865 void CallVehicleTicks()
00866 {
00867 _vehicles_to_autoreplace.Clear();
00868
00869 RunVehicleDayProc();
00870
00871 Station *st;
00872 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00873
00874 Vehicle *v;
00875 FOR_ALL_VEHICLES(v) {
00876
00877 if (!v->Tick()) {
00878 assert(Vehicle::Get(vehicle_index) == NULL);
00879 continue;
00880 }
00881
00882 assert(Vehicle::Get(vehicle_index) == v);
00883
00884 switch (v->type) {
00885 default: break;
00886
00887 case VEH_TRAIN:
00888 case VEH_ROAD:
00889 case VEH_AIRCRAFT:
00890 case VEH_SHIP: {
00891 Vehicle *front = v->First();
00892
00893 if (v->vcache.cached_cargo_age_period != 0) {
00894 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
00895 if (--v->cargo_age_counter == 0) {
00896 v->cargo.AgeCargo();
00897 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
00898 }
00899 }
00900
00901
00902 if (front->vehstatus & VS_CRASHED) continue;
00903
00904
00905 if (v->vehstatus & VS_HIDDEN) continue;
00906
00907
00908 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
00909
00910
00911 switch (v->type) {
00912 case VEH_TRAIN:
00913 if (Train::From(v)->IsWagon()) continue;
00914 break;
00915
00916 case VEH_ROAD:
00917 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
00918 break;
00919
00920 case VEH_AIRCRAFT:
00921 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
00922 break;
00923
00924 default:
00925 break;
00926 }
00927
00928 v->motion_counter += front->cur_speed;
00929
00930 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00931
00932
00933 if (GB(v->tick_counter, 0, 4) == 0) {
00934
00935 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
00936 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
00937 }
00938
00939 break;
00940 }
00941 }
00942 }
00943
00944 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00945 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00946 v = it->first;
00947
00948 cur_company.Change(v->owner);
00949
00950
00951
00952
00953 if (it->second) v->vehstatus &= ~VS_STOPPED;
00954
00955
00956 int x = v->x_pos;
00957 int y = v->y_pos;
00958 int z = v->z_pos;
00959
00960 const Company *c = Company::Get(_current_company);
00961 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00962 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00963 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00964
00965 if (!IsLocalCompany()) continue;
00966
00967 if (res.Succeeded()) {
00968 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00969 continue;
00970 }
00971
00972 StringID error_message = res.GetErrorMessage();
00973 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00974
00975 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00976
00977 StringID message;
00978 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00979 message = error_message;
00980 } else {
00981 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00982 }
00983
00984 SetDParam(0, v->index);
00985 SetDParam(1, error_message);
00986 AddVehicleAdviceNewsItem(message, v->index);
00987 }
00988
00989 cur_company.Restore();
00990 }
00991
00996 static void DoDrawVehicle(const Vehicle *v)
00997 {
00998 SpriteID image = v->cur_image;
00999 PaletteID pal = PAL_NONE;
01000
01001 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
01002
01003
01004 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
01005
01006 if (v->type == VEH_EFFECT) {
01007
01008
01009 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
01010 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
01011 }
01012
01013 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
01014 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
01015 }
01016
01021 void ViewportAddVehicles(DrawPixelInfo *dpi)
01022 {
01023
01024 const int l = dpi->left;
01025 const int r = dpi->left + dpi->width;
01026 const int t = dpi->top;
01027 const int b = dpi->top + dpi->height;
01028
01029
01030 int xl, xu, yl, yu;
01031
01032 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
01033 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
01034 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
01035 } else {
01036
01037 xl = 0;
01038 xu = 0x3F;
01039 }
01040
01041 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
01042 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
01043 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
01044 } else {
01045
01046 yl = 0;
01047 yu = 0x3F << 6;
01048 }
01049
01050 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
01051 for (int x = xl;; x = (x + 1) & 0x3F) {
01052 const Vehicle *v = _vehicle_viewport_hash[x + y];
01053
01054 while (v != NULL) {
01055 if (!(v->vehstatus & VS_HIDDEN) &&
01056 l <= v->coord.right &&
01057 t <= v->coord.bottom &&
01058 r >= v->coord.left &&
01059 b >= v->coord.top) {
01060 DoDrawVehicle(v);
01061 }
01062 v = v->hash_viewport_next;
01063 }
01064
01065 if (x == xu) break;
01066 }
01067
01068 if (y == yu) break;
01069 }
01070 }
01071
01079 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01080 {
01081 Vehicle *found = NULL, *v;
01082 uint dist, best_dist = UINT_MAX;
01083
01084 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01085
01086 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01087 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01088
01089 FOR_ALL_VEHICLES(v) {
01090 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
01091 x >= v->coord.left && x <= v->coord.right &&
01092 y >= v->coord.top && y <= v->coord.bottom) {
01093
01094 dist = max(
01095 abs(((v->coord.left + v->coord.right) >> 1) - x),
01096 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01097 );
01098
01099 if (dist < best_dist) {
01100 found = v;
01101 best_dist = dist;
01102 }
01103 }
01104 }
01105
01106 return found;
01107 }
01108
01113 void DecreaseVehicleValue(Vehicle *v)
01114 {
01115 v->value -= v->value >> 8;
01116 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01117 }
01118
01119 static const byte _breakdown_chance[64] = {
01120 3, 3, 3, 3, 3, 3, 3, 3,
01121 4, 4, 5, 5, 6, 6, 7, 7,
01122 8, 8, 9, 9, 10, 10, 11, 11,
01123 12, 13, 13, 13, 13, 14, 15, 16,
01124 17, 19, 21, 25, 28, 31, 34, 37,
01125 40, 44, 48, 52, 56, 60, 64, 68,
01126 72, 80, 90, 100, 110, 120, 130, 140,
01127 150, 170, 190, 210, 230, 250, 250, 250,
01128 };
01129
01130 void CheckVehicleBreakdown(Vehicle *v)
01131 {
01132 int rel, rel_old;
01133
01134
01135 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01136 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01137
01138 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01139 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01140 v->cur_speed < 5 || _game_mode == GM_MENU) {
01141 return;
01142 }
01143
01144 uint32 r = Random();
01145
01146
01147 int chance = v->breakdown_chance + 1;
01148 if (Chance16I(1, 25, r)) chance += 25;
01149 v->breakdown_chance = min(255, chance);
01150
01151
01152 rel = v->reliability;
01153 if (v->type == VEH_SHIP) rel += 0x6666;
01154
01155
01156 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01157
01158
01159 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01160 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01161 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01162 v->breakdown_chance = 0;
01163 }
01164 }
01165
01172 bool Vehicle::HandleBreakdown()
01173 {
01174
01175
01176
01177
01178
01179 switch (this->breakdown_ctr) {
01180 case 0:
01181 return false;
01182
01183 case 2:
01184 this->breakdown_ctr = 1;
01185
01186 if (this->breakdowns_since_last_service != 255) {
01187 this->breakdowns_since_last_service++;
01188 }
01189
01190 if (this->type == VEH_AIRCRAFT) {
01191
01192 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01193 } else {
01194 this->cur_speed = 0;
01195
01196 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01197 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01198 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01199 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01200 }
01201
01202 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
01203 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01204 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01205 }
01206 }
01207
01208 this->MarkDirty();
01209 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01210 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01211
01212
01213 case 1:
01214
01215 if (this->type == VEH_AIRCRAFT) return false;
01216
01217
01218 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01219 if (--this->breakdown_delay == 0) {
01220 this->breakdown_ctr = 0;
01221 this->MarkDirty();
01222 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01223 }
01224 }
01225 return true;
01226
01227 default:
01228 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01229 return false;
01230 }
01231 }
01232
01237 void AgeVehicle(Vehicle *v)
01238 {
01239 if (v->age < MAX_DAY) {
01240 v->age++;
01241 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
01242 }
01243
01244 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
01245
01246 int age = v->age - v->max_age;
01247 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01248 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01249 v->reliability_spd_dec <<= 1;
01250 }
01251
01252 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01253
01254
01255 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01256
01257
01258 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
01259
01260 StringID str;
01261 if (age == -DAYS_IN_LEAP_YEAR) {
01262 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01263 } else if (age == 0) {
01264 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01265 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01266 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01267 } else {
01268 return;
01269 }
01270
01271 SetDParam(0, v->index);
01272 AddVehicleAdviceNewsItem(str, v->index);
01273 }
01274
01281 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
01282 {
01283 int count = 0;
01284 int max = 0;
01285 int cars = 0;
01286 int unloading = 0;
01287 bool loading = false;
01288
01289 bool is_loading = front->current_order.IsType(OT_LOADING);
01290
01291
01292 const Station *st = Station::GetIfValid(front->last_station_visited);
01293 assert(colour == NULL || (st != NULL && is_loading));
01294
01295 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
01296 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
01297
01298
01299 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
01300 count += v->cargo.Count();
01301 max += v->cargo_cap;
01302 if (v->cargo_cap != 0 && colour != NULL) {
01303 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01304 loading |= !order_no_load &&
01305 (order_full_load || HasBit(st->goods[v->cargo_type].acceptance_pickup, GoodsEntry::GES_PICKUP)) &&
01306 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
01307 cars++;
01308 }
01309 }
01310
01311 if (colour != NULL) {
01312 if (unloading == 0 && loading) {
01313 *colour = STR_PERCENT_UP;
01314 } else if (unloading == 0 && !loading) {
01315 *colour = STR_PERCENT_NONE;
01316 } else if (cars == unloading || !loading) {
01317 *colour = STR_PERCENT_DOWN;
01318 } else {
01319 *colour = STR_PERCENT_UP_DOWN;
01320 }
01321 }
01322
01323
01324 if (max == 0) return 100;
01325
01326
01327 return (count * 100) / max;
01328 }
01329
01334 void VehicleEnterDepot(Vehicle *v)
01335 {
01336
01337 assert(v == v->First());
01338
01339 switch (v->type) {
01340 case VEH_TRAIN: {
01341 Train *t = Train::From(v);
01342 SetWindowClassesDirty(WC_TRAINS_LIST);
01343
01344 SetDepotReservation(t->tile, false);
01345 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01346
01347 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01348 t->wait_counter = 0;
01349 t->force_proceed = TFP_NONE;
01350 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01351 t->ConsistChanged(true);
01352 break;
01353 }
01354
01355 case VEH_ROAD:
01356 SetWindowClassesDirty(WC_ROADVEH_LIST);
01357 break;
01358
01359 case VEH_SHIP: {
01360 SetWindowClassesDirty(WC_SHIPS_LIST);
01361 Ship *ship = Ship::From(v);
01362 ship->state = TRACK_BIT_DEPOT;
01363 ship->UpdateCache();
01364 ship->UpdateViewport(true, true);
01365 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01366 break;
01367 }
01368
01369 case VEH_AIRCRAFT:
01370 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01371 HandleAircraftEnterHangar(Aircraft::From(v));
01372 break;
01373 default: NOT_REACHED();
01374 }
01375 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01376
01377 if (v->type != VEH_TRAIN) {
01378
01379
01380 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01381 }
01382 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01383
01384 v->vehstatus |= VS_HIDDEN;
01385 v->cur_speed = 0;
01386
01387 VehicleServiceInDepot(v);
01388
01389
01390 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01391 v->MarkDirty();
01392
01393 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01394 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01395
01396 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01397 Order t = v->current_order;
01398 v->current_order.MakeDummy();
01399
01400
01401
01402 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01403 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01404 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01405
01406 return;
01407 }
01408
01409 if (t.IsRefit()) {
01410 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01411 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01412 cur_company.Restore();
01413
01414 if (cost.Failed()) {
01415 _vehicles_to_autoreplace[v] = false;
01416 if (v->owner == _local_company) {
01417
01418 SetDParam(0, v->index);
01419 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
01420 }
01421 } else if (cost.GetCost() != 0) {
01422 v->profit_this_year -= cost.GetCost() << 8;
01423 if (v->owner == _local_company) {
01424 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01425 }
01426 }
01427 }
01428
01429 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01430
01431 v->DeleteUnreachedImplicitOrders();
01432 UpdateVehicleTimetable(v, true);
01433 v->IncrementImplicitOrderIndex();
01434 }
01435 if (t.GetDepotActionType() & ODATFB_HALT) {
01436
01437 _vehicles_to_autoreplace[v] = false;
01438 if (v->owner == _local_company) {
01439 SetDParam(0, v->index);
01440 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
01441 }
01442 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01443 }
01444 }
01445 }
01446
01447
01453 void VehicleUpdatePosition(Vehicle *v)
01454 {
01455 UpdateVehicleTileHash(v, false);
01456 }
01457
01464 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01465 {
01466 int img = v->cur_image;
01467 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01468 const Sprite *spr = GetSprite(img, ST_NORMAL);
01469
01470 pt.x += spr->x_offs;
01471 pt.y += spr->y_offs;
01472
01473 UpdateVehicleViewportHash(v, pt.x, pt.y);
01474
01475 Rect old_coord = v->coord;
01476 v->coord.left = pt.x;
01477 v->coord.top = pt.y;
01478 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01479 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01480
01481 if (dirty) {
01482 if (old_coord.left == INVALID_COORD) {
01483 MarkSingleVehicleDirty(v);
01484 } else {
01485 MarkAllViewportsDirty(
01486 min(old_coord.left, v->coord.left),
01487 min(old_coord.top, v->coord.top),
01488 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01489 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01490 );
01491 }
01492 }
01493 }
01494
01499 void VehicleUpdatePositionAndViewport(Vehicle *v)
01500 {
01501 VehicleUpdatePosition(v);
01502 VehicleUpdateViewport(v, true);
01503 }
01504
01509 void MarkSingleVehicleDirty(const Vehicle *v)
01510 {
01511 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01512 }
01513
01519 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01520 {
01521 static const int8 _delta_coord[16] = {
01522 -1,-1,-1, 0, 1, 1, 1, 0,
01523 -1, 0, 1, 1, 1, 0,-1,-1,
01524 };
01525
01526 int x = v->x_pos + _delta_coord[v->direction];
01527 int y = v->y_pos + _delta_coord[v->direction + 8];
01528
01529 GetNewVehiclePosResult gp;
01530 gp.x = x;
01531 gp.y = y;
01532 gp.old_tile = v->tile;
01533 gp.new_tile = TileVirtXY(x, y);
01534 return gp;
01535 }
01536
01537 static const Direction _new_direction_table[] = {
01538 DIR_N, DIR_NW, DIR_W,
01539 DIR_NE, DIR_SE, DIR_SW,
01540 DIR_E, DIR_SE, DIR_S
01541 };
01542
01543 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01544 {
01545 int i = 0;
01546
01547 if (y >= v->y_pos) {
01548 if (y != v->y_pos) i += 3;
01549 i += 3;
01550 }
01551
01552 if (x >= v->x_pos) {
01553 if (x != v->x_pos) i++;
01554 i++;
01555 }
01556
01557 Direction dir = v->direction;
01558
01559 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01560 if (dirdiff == DIRDIFF_SAME) return dir;
01561 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01562 }
01563
01573 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01574 {
01575 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01576 }
01577
01585 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01586 {
01587
01588 const Vehicle *v;
01589 FOR_ALL_VEHICLES(v) {
01590 if (v->type == type && v->owner == owner) {
01591 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01592 }
01593 }
01594
01595 if (this->maxid == 0) return;
01596
01597
01598
01599
01600 this->cache = CallocT<bool>(this->maxid + 2);
01601
01602
01603 FOR_ALL_VEHICLES(v) {
01604 if (v->type == type && v->owner == owner) {
01605 this->cache[v->unitnumber] = true;
01606 }
01607 }
01608 }
01609
01611 UnitID FreeUnitIDGenerator::NextID()
01612 {
01613 if (this->maxid <= this->curid) return ++this->curid;
01614
01615 while (this->cache[++this->curid]) { }
01616
01617 return this->curid;
01618 }
01619
01625 UnitID GetFreeUnitNumber(VehicleType type)
01626 {
01627
01628 uint max_veh;
01629 switch (type) {
01630 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01631 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01632 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01633 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01634 default: NOT_REACHED();
01635 }
01636
01637 const Company *c = Company::Get(_current_company);
01638 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01639
01640 FreeUnitIDGenerator gen(type, _current_company);
01641
01642 return gen.NextID();
01643 }
01644
01645
01654 bool CanBuildVehicleInfrastructure(VehicleType type)
01655 {
01656 assert(IsCompanyBuildableVehicleType(type));
01657
01658 if (!Company::IsValidID(_local_company)) return false;
01659 if (!_settings_client.gui.disable_unsuitable_building) return true;
01660
01661 UnitID max;
01662 switch (type) {
01663 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01664 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01665 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01666 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01667 default: NOT_REACHED();
01668 }
01669
01670
01671 if (max > 0) {
01672
01673 const Engine *e;
01674 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01675 if (HasBit(e->company_avail, _local_company)) return true;
01676 }
01677 return false;
01678 }
01679
01680
01681 const Vehicle *v;
01682 FOR_ALL_VEHICLES(v) {
01683 if (v->owner == _local_company && v->type == type) return true;
01684 }
01685
01686 return false;
01687 }
01688
01689
01697 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01698 {
01699 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01700 const Engine *e = Engine::Get(engine_type);
01701 switch (e->type) {
01702 default: NOT_REACHED();
01703 case VEH_TRAIN:
01704 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01705
01706
01707 engine_type = parent_engine_type;
01708 e = Engine::Get(engine_type);
01709
01710 }
01711
01712 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01713 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01714 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01715 if (!CargoSpec::Get(cargo_type)->is_freight) {
01716 if (parent_engine_type == INVALID_ENGINE) {
01717 return LS_PASSENGER_WAGON_STEAM;
01718 } else {
01719 switch (RailVehInfo(parent_engine_type)->engclass) {
01720 default: NOT_REACHED();
01721 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01722 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01723 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01724 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01725 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01726 }
01727 }
01728 } else {
01729 return LS_FREIGHT_WAGON;
01730 }
01731 } else {
01732 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01733
01734 switch (e->u.rail.engclass) {
01735 default: NOT_REACHED();
01736 case EC_STEAM: return LS_STEAM;
01737 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01738 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01739 case EC_MONORAIL: return LS_MONORAIL;
01740 case EC_MAGLEV: return LS_MAGLEV;
01741 }
01742 }
01743
01744 case VEH_ROAD:
01745
01746 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01747 engine_type = parent_engine_type;
01748 e = Engine::Get(engine_type);
01749 cargo_type = v->First()->cargo_type;
01750 }
01751 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01752 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01753
01754
01755 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01756
01757 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01758 } else {
01759
01760 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01761 }
01762
01763 case VEH_SHIP:
01764 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01765 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01766 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01767
01768 case VEH_AIRCRAFT:
01769 switch (e->u.air.subtype) {
01770 case AIR_HELI: return LS_HELICOPTER;
01771 case AIR_CTOL: return LS_SMALL_PLANE;
01772 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01773 default: NOT_REACHED();
01774 }
01775 }
01776 }
01777
01787 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01788 {
01789 const Company *c = Company::Get(company);
01790 LiveryScheme scheme = LS_DEFAULT;
01791
01792
01793
01794 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01795
01796 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01797
01798
01799 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01800 }
01801
01802 return &c->livery[scheme];
01803 }
01804
01805
01806 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01807 {
01808 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01809
01810
01811 if (map != PAL_NONE) return map;
01812
01813 const Engine *e = Engine::Get(engine_type);
01814
01815
01816 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01817 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01818
01819 if (callback != CALLBACK_FAILED) {
01820 assert_compile(PAL_NONE == 0);
01821 map = GB(callback, 0, 14);
01822
01823
01824 if (!HasBit(callback, 14)) {
01825
01826 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01827 return map;
01828 }
01829 }
01830 }
01831
01832 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01833
01834 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01835
01836
01837 if (!Company::IsValidID(company)) return map;
01838
01839 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01840
01841 map += livery->colour1;
01842 if (twocc) map += livery->colour2 * 16;
01843
01844
01845 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01846 return map;
01847 }
01848
01855 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01856 {
01857 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01858 }
01859
01865 PaletteID GetVehiclePalette(const Vehicle *v)
01866 {
01867 if (v->IsGroundVehicle()) {
01868 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01869 }
01870
01871 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01872 }
01873
01877 void Vehicle::DeleteUnreachedImplicitOrders()
01878 {
01879 if (this->IsGroundVehicle()) {
01880 uint16 &gv_flags = this->GetGroundVehicleFlags();
01881 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01882
01883 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01884 this->cur_implicit_order_index = this->cur_real_order_index;
01885 InvalidateVehicleOrder(this, 0);
01886 return;
01887 }
01888 }
01889
01890 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01891 while (order != NULL) {
01892 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01893
01894 if (order->IsType(OT_IMPLICIT)) {
01895 DeleteOrder(this, this->cur_implicit_order_index);
01896
01897 order = this->GetOrder(this->cur_implicit_order_index);
01898 } else {
01899
01900 order = order->next;
01901 this->cur_implicit_order_index++;
01902 }
01903
01904
01905 if (order == NULL) {
01906 order = this->GetOrder(0);
01907 this->cur_implicit_order_index = 0;
01908 }
01909 }
01910 }
01911
01916 void Vehicle::BeginLoading()
01917 {
01918 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01919
01920 if (this->current_order.IsType(OT_GOTO_STATION) &&
01921 this->current_order.GetDestination() == this->last_station_visited) {
01922 this->DeleteUnreachedImplicitOrders();
01923
01924
01925 this->current_order.MakeLoading(true);
01926 UpdateVehicleTimetable(this, true);
01927
01928
01929
01930
01931
01932
01933 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01934
01935 } else {
01936
01937
01938
01939
01940
01941 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01942 if (this->IsGroundVehicle() && in_list != NULL &&
01943 (!in_list->IsType(OT_IMPLICIT) ||
01944 in_list->GetDestination() != this->last_station_visited)) {
01945 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01946
01947 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01948 if (prev_order == NULL ||
01949 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01950 prev_order->GetDestination() != this->last_station_visited) {
01951
01952
01953
01954 int target_index = this->cur_implicit_order_index;
01955 bool found = false;
01956 while (target_index != this->cur_real_order_index) {
01957 const Order *order = this->GetOrder(target_index);
01958 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01959 found = true;
01960 break;
01961 }
01962 target_index++;
01963 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01964 assert(target_index != this->cur_implicit_order_index);
01965 }
01966
01967 if (found) {
01968 if (suppress_implicit_orders) {
01969
01970 this->cur_implicit_order_index = target_index;
01971 InvalidateVehicleOrder(this, 0);
01972 } else {
01973
01974 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01975 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01976 if (order->IsType(OT_IMPLICIT)) {
01977 DeleteOrder(this, this->cur_implicit_order_index);
01978
01979 order = this->GetOrder(this->cur_implicit_order_index);
01980 } else {
01981
01982 order = order->next;
01983 this->cur_implicit_order_index++;
01984 }
01985
01986
01987 if (order == NULL) {
01988 order = this->GetOrder(0);
01989 this->cur_implicit_order_index = 0;
01990 }
01991 assert(order != NULL);
01992 }
01993 }
01994 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01995
01996 Order *implicit_order = new Order();
01997 implicit_order->MakeImplicit(this->last_station_visited);
01998 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01999 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
02000
02001
02002
02003 uint16 &gv_flags = this->GetGroundVehicleFlags();
02004 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02005 }
02006 }
02007 }
02008 this->current_order.MakeLoading(false);
02009 }
02010
02011 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
02012
02013 PrepareUnload(this);
02014
02015 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
02016 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02017 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
02018 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
02019
02020 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
02021 this->cur_speed = 0;
02022 this->MarkDirty();
02023 }
02024
02029 void Vehicle::LeaveStation()
02030 {
02031 assert(this->current_order.IsType(OT_LOADING));
02032
02033 delete this->cargo_payment;
02034
02035
02036 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
02037
02038 this->current_order.MakeLeaveStation();
02039 Station *st = Station::Get(this->last_station_visited);
02040 st->loading_vehicles.remove(this);
02041
02042 HideFillingPercent(&this->fill_percent_te_id);
02043
02044 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02045
02046 if (IsTileType(this->tile, MP_STATION)) {
02047 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
02048 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02049 }
02050
02051 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02052 }
02053 }
02054
02055
02061 void Vehicle::HandleLoading(bool mode)
02062 {
02063 switch (this->current_order.GetType()) {
02064 case OT_LOADING: {
02065 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02066
02067
02068 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02069
02070 this->PlayLeaveStationSound();
02071
02072 this->LeaveStation();
02073
02074
02075 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02076 if (order == NULL ||
02077 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02078 order->GetDestination() != this->last_station_visited) {
02079 return;
02080 }
02081 break;
02082 }
02083
02084 case OT_DUMMY: break;
02085
02086 default: return;
02087 }
02088
02089 this->IncrementImplicitOrderIndex();
02090 }
02091
02098 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02099 {
02100 CommandCost ret = CheckOwnership(this->owner);
02101 if (ret.Failed()) return ret;
02102
02103 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02104 if (this->IsStoppedInDepot()) return CMD_ERROR;
02105
02106 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02107 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02108 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02109
02110
02111
02112 if (flags & DC_EXEC) {
02113 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02114 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02115 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02116 }
02117 return CommandCost();
02118 }
02119
02120 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02121 if (flags & DC_EXEC) {
02122
02123
02124 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02125
02126 if (this->IsGroundVehicle()) {
02127 uint16 &gv_flags = this->GetGroundVehicleFlags();
02128 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02129 }
02130
02131 this->current_order.MakeDummy();
02132 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02133 }
02134 return CommandCost();
02135 }
02136
02137 TileIndex location;
02138 DestinationID destination;
02139 bool reverse;
02140 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};
02141 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02142
02143 if (flags & DC_EXEC) {
02144 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02145
02146 if (this->IsGroundVehicle()) {
02147 uint16 &gv_flags = this->GetGroundVehicleFlags();
02148 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02149 }
02150
02151 this->dest_tile = location;
02152 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02153 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02154 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02155
02156
02157 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02158
02159 if (this->type == VEH_AIRCRAFT) {
02160 Aircraft *a = Aircraft::From(this);
02161 if (a->state == FLYING && a->targetairport != destination) {
02162
02163 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02164 AircraftNextAirportPos_and_Order(a);
02165 }
02166 }
02167 }
02168
02169 return CommandCost();
02170
02171 }
02172
02177 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02178 {
02179 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02180 const Engine *e = this->GetEngine();
02181
02182
02183 byte visual_effect;
02184 switch (e->type) {
02185 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02186 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02187 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02188 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02189 }
02190
02191
02192 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02193 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02194
02195 if (callback != CALLBACK_FAILED) {
02196 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02197
02198 callback = GB(callback, 0, 8);
02199
02200
02201 if (callback == VE_DEFAULT) {
02202 assert(HasBit(callback, VE_DISABLE_EFFECT));
02203 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02204 }
02205 visual_effect = callback;
02206 }
02207 }
02208
02209
02210 if (visual_effect == VE_DEFAULT ||
02211 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02212
02213
02214 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02215 if (visual_effect == VE_DEFAULT) {
02216 visual_effect = 1 << VE_DISABLE_EFFECT;
02217 } else {
02218 SetBit(visual_effect, VE_DISABLE_EFFECT);
02219 }
02220 } else {
02221 if (visual_effect == VE_DEFAULT) {
02222
02223 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02224 }
02225 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02226 }
02227 }
02228
02229 this->vcache.cached_vis_effect = visual_effect;
02230
02231 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02232 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02233 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02234 }
02235 }
02236
02237 static const int8 _vehicle_smoke_pos[8] = {
02238 1, 1, 1, 0, -1, -1, -1, 0
02239 };
02240
02245 void Vehicle::ShowVisualEffect() const
02246 {
02247 assert(this->IsPrimaryVehicle());
02248 bool sound = false;
02249
02250
02251
02252
02253
02254
02255 if (_settings_game.vehicle.smoke_amount == 0 ||
02256 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02257 this->cur_speed < 2) {
02258 return;
02259 }
02260
02261 uint max_speed = this->vcache.cached_max_speed;
02262 if (this->type == VEH_TRAIN) {
02263 const Train *t = Train::From(this);
02264
02265
02266
02267
02268 if (HasBit(t->flags, VRF_REVERSING) ||
02269 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02270 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02271 return;
02272 }
02273
02274 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02275 max_speed = min(max_speed, this->current_order.max_speed);
02276 }
02277 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02278
02279 const Vehicle *v = this;
02280
02281 do {
02282 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02283 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02284 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02285
02286
02287
02288
02289
02290
02291
02292
02293 if (disable_effect ||
02294 v->vehstatus & VS_HIDDEN ||
02295 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02296 IsDepotTile(v->tile) ||
02297 IsTunnelTile(v->tile) ||
02298 (v->type == VEH_TRAIN &&
02299 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02300 continue;
02301 }
02302
02303
02304
02305
02306 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02307
02308 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02309 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02310
02311 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02312 x = -x;
02313 y = -y;
02314 }
02315
02316 switch (effect_type) {
02317 case VE_TYPE_STEAM:
02318
02319
02320
02321
02322
02323 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02324 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02325 sound = true;
02326 }
02327 break;
02328
02329 case VE_TYPE_DIESEL: {
02330
02331
02332
02333
02334
02335
02336
02337
02338
02339
02340
02341 int power_weight_effect = 0;
02342 if (v->type == VEH_TRAIN) {
02343 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02344 }
02345 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02346 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02347 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02348 sound = true;
02349 }
02350 break;
02351 }
02352
02353 case VE_TYPE_ELECTRIC:
02354
02355
02356
02357
02358
02359
02360 if (GB(v->tick_counter, 0, 2) == 0 &&
02361 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02362 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02363 sound = true;
02364 }
02365 break;
02366
02367 default:
02368 break;
02369 }
02370 } while ((v = v->Next()) != NULL);
02371
02372 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02373 }
02374
02379 void Vehicle::SetNext(Vehicle *next)
02380 {
02381 assert(this != next);
02382
02383 if (this->next != NULL) {
02384
02385 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02386 v->first = this->next;
02387 }
02388 this->next->previous = NULL;
02389 }
02390
02391 this->next = next;
02392
02393 if (this->next != NULL) {
02394
02395 if (this->next->previous != NULL) this->next->previous->next = NULL;
02396 this->next->previous = this;
02397 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02398 v->first = this->first;
02399 }
02400 }
02401 }
02402
02408 void Vehicle::AddToShared(Vehicle *shared_chain)
02409 {
02410 assert(this->previous_shared == NULL && this->next_shared == NULL);
02411
02412 if (shared_chain->orders.list == NULL) {
02413 assert(shared_chain->previous_shared == NULL);
02414 assert(shared_chain->next_shared == NULL);
02415 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02416 }
02417
02418 this->next_shared = shared_chain->next_shared;
02419 this->previous_shared = shared_chain;
02420
02421 shared_chain->next_shared = this;
02422
02423 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02424
02425 shared_chain->orders.list->AddVehicle(this);
02426 }
02427
02431 void Vehicle::RemoveFromShared()
02432 {
02433
02434
02435 bool were_first = (this->FirstShared() == this);
02436 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02437
02438 this->orders.list->RemoveVehicle(this);
02439
02440 if (!were_first) {
02441
02442 this->previous_shared->next_shared = this->NextShared();
02443 }
02444
02445 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02446
02447
02448 if (this->orders.list->GetNumVehicles() == 1) {
02449
02450 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02451 InvalidateVehicleOrder(this->FirstShared(), 0);
02452 } else if (were_first) {
02453
02454
02455 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02456 }
02457
02458 this->next_shared = NULL;
02459 this->previous_shared = NULL;
02460 }
02461
02462 void VehiclesYearlyLoop()
02463 {
02464 Vehicle *v;
02465 FOR_ALL_VEHICLES(v) {
02466 if (v->IsPrimaryVehicle()) {
02467
02468 Money profit = v->GetDisplayProfitThisYear();
02469 if (v->age >= 730 && profit < 0) {
02470 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02471 SetDParam(0, v->index);
02472 SetDParam(1, profit);
02473 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
02474 }
02475 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02476 }
02477
02478 v->profit_last_year = v->profit_this_year;
02479 v->profit_this_year = 0;
02480 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02481 }
02482 }
02483 GroupStatistics::UpdateProfits();
02484 SetWindowClassesDirty(WC_TRAINS_LIST);
02485 SetWindowClassesDirty(WC_SHIPS_LIST);
02486 SetWindowClassesDirty(WC_ROADVEH_LIST);
02487 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02488 }
02489
02490
02500 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02501 {
02502 const Engine *e = Engine::GetIfValid(engine_type);
02503 assert(e != NULL);
02504
02505 switch (e->type) {
02506 case VEH_TRAIN:
02507 return (st->facilities & FACIL_TRAIN) != 0;
02508
02509 case VEH_ROAD:
02510
02511
02512
02513 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02514
02515 case VEH_SHIP:
02516 return (st->facilities & FACIL_DOCK) != 0;
02517
02518 case VEH_AIRCRAFT:
02519 return (st->facilities & FACIL_AIRPORT) != 0 &&
02520 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02521
02522 default:
02523 return false;
02524 }
02525 }
02526
02533 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02534 {
02535 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02536
02537 return CanVehicleUseStation(v->engine_type, st);
02538 }
02539
02545 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02546 {
02547 assert(this->IsGroundVehicle());
02548 if (this->type == VEH_TRAIN) {
02549 return &Train::From(this)->gcache;
02550 } else {
02551 return &RoadVehicle::From(this)->gcache;
02552 }
02553 }
02554
02560 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02561 {
02562 assert(this->IsGroundVehicle());
02563 if (this->type == VEH_TRAIN) {
02564 return &Train::From(this)->gcache;
02565 } else {
02566 return &RoadVehicle::From(this)->gcache;
02567 }
02568 }
02569
02575 uint16 &Vehicle::GetGroundVehicleFlags()
02576 {
02577 assert(this->IsGroundVehicle());
02578 if (this->type == VEH_TRAIN) {
02579 return Train::From(this)->gv_flags;
02580 } else {
02581 return RoadVehicle::From(this)->gv_flags;
02582 }
02583 }
02584
02590 const uint16 &Vehicle::GetGroundVehicleFlags() const
02591 {
02592 assert(this->IsGroundVehicle());
02593 if (this->type == VEH_TRAIN) {
02594 return Train::From(this)->gv_flags;
02595 } else {
02596 return RoadVehicle::From(this)->gv_flags;
02597 }
02598 }
02599
02608 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02609 {
02610 if (v->type == VEH_TRAIN) {
02611 Train *u = Train::From(v);
02612
02613 u = u->GetFirstEnginePart();
02614
02615
02616 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02617 do {
02618
02619 set.Include(u->index);
02620
02621
02622 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02623
02624 u = u->Next();
02625 } while (u != NULL && u->IsArticulatedPart());
02626 }
02627 }
02628 }