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 return pass;
00198 }
00199
00200
00209 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00210 {
00211 const Engine *e = Engine::Get(engine);
00212 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
00213
00214 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00215 SetBit(grfconfig->grf_bugs, bug_type);
00216 SetDParamStr(0, grfconfig->GetName());
00217 SetDParam(1, engine);
00218 ShowErrorMessage(part1, part2, WL_CRITICAL);
00219 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00220 }
00221
00222
00223 char buffer[512];
00224
00225 SetDParamStr(0, grfconfig->GetName());
00226 GetString(buffer, part1, lastof(buffer));
00227 DEBUG(grf, 0, "%s", buffer + 3);
00228
00229 SetDParam(1, engine);
00230 GetString(buffer, part2, lastof(buffer));
00231 DEBUG(grf, 0, "%s", buffer + 3);
00232 }
00233
00239 void VehicleLengthChanged(const Vehicle *u)
00240 {
00241
00242 const Engine *engine = u->GetEngine();
00243 uint32 grfid = engine->grf_prop.grffile->grfid;
00244 GRFConfig *grfconfig = GetGRFConfig(grfid);
00245 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
00246 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
00247 }
00248 }
00249
00254 Vehicle::Vehicle(VehicleType type)
00255 {
00256 this->type = type;
00257 this->coord.left = INVALID_COORD;
00258 this->group_id = DEFAULT_GROUP;
00259 this->fill_percent_te_id = INVALID_TE_ID;
00260 this->first = this;
00261 this->colourmap = PAL_NONE;
00262 this->cargo_age_counter = 1;
00263 }
00264
00269 byte VehicleRandomBits()
00270 {
00271 return GB(Random(), 0, 8);
00272 }
00273
00274
00275
00276 const int HASH_BITS = 7;
00277 const int HASH_SIZE = 1 << HASH_BITS;
00278 const int HASH_MASK = HASH_SIZE - 1;
00279 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00280 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00281
00282
00283
00284 const int HASH_RES = 0;
00285
00286 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
00287
00288 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00289 {
00290 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00291 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00292 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00293 for (; v != NULL; v = v->hash_tile_next) {
00294 Vehicle *a = proc(v, data);
00295 if (find_first && a != NULL) return a;
00296 }
00297 if (x == xu) break;
00298 }
00299 if (y == yu) break;
00300 }
00301
00302 return NULL;
00303 }
00304
00305
00317 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00318 {
00319 const int COLL_DIST = 6;
00320
00321
00322 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00323 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00324 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00325 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00326
00327 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
00328 }
00329
00344 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00345 {
00346 VehicleFromPosXY(x, y, data, proc, false);
00347 }
00348
00360 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00361 {
00362 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00363 }
00364
00375 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00376 {
00377 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00378 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00379
00380 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00381 for (; v != NULL; v = v->hash_tile_next) {
00382 if (v->tile != tile) continue;
00383
00384 Vehicle *a = proc(v, data);
00385 if (find_first && a != NULL) return a;
00386 }
00387
00388 return NULL;
00389 }
00390
00404 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00405 {
00406 VehicleFromPos(tile, data, proc, false);
00407 }
00408
00419 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00420 {
00421 return VehicleFromPos(tile, data, proc, true) != NULL;
00422 }
00423
00430 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00431 {
00432 int z = *(int*)data;
00433
00434 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00435 if (v->z_pos > z) return NULL;
00436
00437 return v;
00438 }
00439
00445 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00446 {
00447 int z = GetTileMaxPixelZ(tile);
00448
00449
00450
00451
00452
00453 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00454 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00455 return CommandCost();
00456 }
00457
00459 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00460 {
00461 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00462 if (v == (const Vehicle *)data) return NULL;
00463
00464 return v;
00465 }
00466
00474 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00475 {
00476
00477
00478
00479
00480 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00481 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00482
00483 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00484 return CommandCost();
00485 }
00486
00487 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00488 {
00489 TrackBits rail_bits = *(TrackBits *)data;
00490
00491 if (v->type != VEH_TRAIN) return NULL;
00492
00493 Train *t = Train::From(v);
00494 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00495
00496 return v;
00497 }
00498
00507 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00508 {
00509
00510
00511
00512
00513 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00514 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00515 return CommandCost();
00516 }
00517
00518 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
00519 {
00520 Vehicle **old_hash = v->hash_tile_current;
00521 Vehicle **new_hash;
00522
00523 if (remove) {
00524 new_hash = NULL;
00525 } else {
00526 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00527 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00528 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00529 }
00530
00531 if (old_hash == new_hash) return;
00532
00533
00534 if (old_hash != NULL) {
00535 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
00536 *v->hash_tile_prev = v->hash_tile_next;
00537 }
00538
00539
00540 if (new_hash != NULL) {
00541 v->hash_tile_next = *new_hash;
00542 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
00543 v->hash_tile_prev = new_hash;
00544 *new_hash = v;
00545 }
00546
00547
00548 v->hash_tile_current = new_hash;
00549 }
00550
00551 static Vehicle *_vehicle_viewport_hash[0x1000];
00552
00553 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
00554 {
00555 Vehicle **old_hash, **new_hash;
00556 int old_x = v->coord.left;
00557 int old_y = v->coord.top;
00558
00559 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
00560 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
00561
00562 if (old_hash == new_hash) return;
00563
00564
00565 if (old_hash != NULL) {
00566 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
00567 *v->hash_viewport_prev = v->hash_viewport_next;
00568 }
00569
00570
00571 if (new_hash != NULL) {
00572 v->hash_viewport_next = *new_hash;
00573 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
00574 v->hash_viewport_prev = new_hash;
00575 *new_hash = v;
00576 }
00577 }
00578
00579 void ResetVehicleHash()
00580 {
00581 Vehicle *v;
00582 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
00583 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
00584 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
00585 }
00586
00587 void ResetVehicleColourMap()
00588 {
00589 Vehicle *v;
00590 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00591 }
00592
00597 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00598 static AutoreplaceMap _vehicles_to_autoreplace;
00599
00600 void InitializeVehicles()
00601 {
00602 _vehicles_to_autoreplace.Reset();
00603 ResetVehicleHash();
00604 }
00605
00606 uint CountVehiclesInChain(const Vehicle *v)
00607 {
00608 uint count = 0;
00609 do count++; while ((v = v->Next()) != NULL);
00610 return count;
00611 }
00612
00617 bool Vehicle::IsEngineCountable() const
00618 {
00619 switch (this->type) {
00620 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00621 case VEH_TRAIN:
00622 return !this->IsArticulatedPart() &&
00623 !Train::From(this)->IsRearDualheaded();
00624 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00625 case VEH_SHIP: return true;
00626 default: return false;
00627 }
00628 }
00629
00634 bool Vehicle::HasEngineType() const
00635 {
00636 switch (this->type) {
00637 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00638 case VEH_TRAIN:
00639 case VEH_ROAD:
00640 case VEH_SHIP: return true;
00641 default: return false;
00642 }
00643 }
00644
00650 const Engine *Vehicle::GetEngine() const
00651 {
00652 return Engine::Get(this->engine_type);
00653 }
00654
00660 const GRFFile *Vehicle::GetGRF() const
00661 {
00662 return this->GetEngine()->GetGRF();
00663 }
00664
00670 uint32 Vehicle::GetGRFID() const
00671 {
00672 return this->GetEngine()->GetGRFID();
00673 }
00674
00682 void Vehicle::HandlePathfindingResult(bool path_found)
00683 {
00684 if (path_found) {
00685
00686 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00687
00688
00689 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00690
00691 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00692 return;
00693 }
00694
00695
00696 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00697
00698
00699 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00700
00701 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
00702 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00703 SetDParam(0, this->index);
00704 AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00705 }
00706 }
00707
00709 void Vehicle::PreDestructor()
00710 {
00711 if (CleaningPool()) return;
00712
00713 if (Station::IsValidID(this->last_station_visited)) {
00714 Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00715
00716 HideFillingPercent(&this->fill_percent_te_id);
00717
00718 delete this->cargo_payment;
00719 }
00720
00721 if (this->IsEngineCountable()) {
00722 GroupStatistics::CountEngine(this, -1);
00723 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
00724 GroupStatistics::UpdateAutoreplace(this->owner);
00725
00726 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00727 DeleteGroupHighlightOfVehicle(this);
00728 }
00729
00730 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00731 Aircraft *a = Aircraft::From(this);
00732 Station *st = GetTargetAirportIfValid(a);
00733 if (st != NULL) {
00734 const AirportFTA *layout = st->airport.GetFTA()->layout;
00735 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00736 }
00737 }
00738
00739
00740 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00741 RoadVehicle *v = RoadVehicle::From(this);
00742 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00743
00744 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00745 }
00746 }
00747
00748 if (this->Previous() == NULL) {
00749 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00750 }
00751
00752 if (this->IsPrimaryVehicle()) {
00753 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00754 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00755 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00756 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00757 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00758 SetWindowDirty(WC_COMPANY, this->owner);
00759 OrderBackup::ClearVehicle(this);
00760 }
00761 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00762
00763 this->cargo.Truncate(0);
00764 DeleteVehicleOrders(this);
00765 DeleteDepotHighlightOfVehicle(this);
00766
00767 extern void StopGlobalFollowVehicle(const Vehicle *v);
00768 StopGlobalFollowVehicle(this);
00769
00770 ReleaseDisastersTargetingVehicle(this->index);
00771 }
00772
00773 Vehicle::~Vehicle()
00774 {
00775 free(this->name);
00776
00777 if (CleaningPool()) {
00778 this->cargo.OnCleanPool();
00779 return;
00780 }
00781
00782
00783
00784 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00785
00786 Vehicle *v = this->Next();
00787 this->SetNext(NULL);
00788
00789 delete v;
00790
00791 UpdateVehicleTileHash(this, true);
00792 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
00793 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00794 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00795 }
00796
00801 void VehicleEnteredDepotThisTick(Vehicle *v)
00802 {
00803
00804 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00805
00806
00807
00808
00809
00810
00811 v->vehstatus |= VS_STOPPED;
00812 }
00813
00819 static void RunVehicleDayProc()
00820 {
00821 if (_game_mode != GM_NORMAL) return;
00822
00823
00824 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00825 Vehicle *v = Vehicle::Get(i);
00826 if (v == NULL) continue;
00827
00828
00829 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
00830 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00831 if (callback != CALLBACK_FAILED) {
00832 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00833 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00834
00835 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
00836 }
00837 }
00838
00839
00840 v->OnNewDay();
00841 }
00842 }
00843
00844 void CallVehicleTicks()
00845 {
00846 _vehicles_to_autoreplace.Clear();
00847
00848 RunVehicleDayProc();
00849
00850 Station *st;
00851 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00852
00853 Vehicle *v;
00854 FOR_ALL_VEHICLES(v) {
00855
00856 if (!v->Tick()) {
00857 assert(Vehicle::Get(vehicle_index) == NULL);
00858 continue;
00859 }
00860
00861 assert(Vehicle::Get(vehicle_index) == v);
00862
00863 switch (v->type) {
00864 default: break;
00865
00866 case VEH_TRAIN:
00867 case VEH_ROAD:
00868 case VEH_AIRCRAFT:
00869 case VEH_SHIP:
00870 if (v->vcache.cached_cargo_age_period != 0) {
00871 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
00872 if (--v->cargo_age_counter == 0) {
00873 v->cargo.AgeCargo();
00874 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
00875 }
00876 }
00877
00878 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00879 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00880 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00881
00882 v->motion_counter += v->cur_speed;
00883
00884 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00885
00886
00887 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00888 }
00889 }
00890
00891 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00892 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00893 v = it->first;
00894
00895 cur_company.Change(v->owner);
00896
00897
00898
00899
00900 if (it->second) v->vehstatus &= ~VS_STOPPED;
00901
00902
00903 int x = v->x_pos;
00904 int y = v->y_pos;
00905 int z = v->z_pos;
00906
00907 const Company *c = Company::Get(_current_company);
00908 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00909 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00910 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00911
00912 if (!IsLocalCompany()) continue;
00913
00914 if (res.Succeeded()) {
00915 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00916 continue;
00917 }
00918
00919 StringID error_message = res.GetErrorMessage();
00920 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00921
00922 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00923
00924 StringID message;
00925 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00926 message = error_message;
00927 } else {
00928 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00929 }
00930
00931 SetDParam(0, v->index);
00932 SetDParam(1, error_message);
00933 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00934 }
00935
00936 cur_company.Restore();
00937 }
00938
00943 static void DoDrawVehicle(const Vehicle *v)
00944 {
00945 SpriteID image = v->cur_image;
00946 PaletteID pal = PAL_NONE;
00947
00948 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00949
00950
00951 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
00952
00953 if (v->type == VEH_EFFECT) {
00954
00955
00956 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00957 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00958 }
00959
00960 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00961 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
00962 }
00963
00968 void ViewportAddVehicles(DrawPixelInfo *dpi)
00969 {
00970
00971 const int l = dpi->left;
00972 const int r = dpi->left + dpi->width;
00973 const int t = dpi->top;
00974 const int b = dpi->top + dpi->height;
00975
00976
00977 int xl, xu, yl, yu;
00978
00979 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
00980 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
00981 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
00982 } else {
00983
00984 xl = 0;
00985 xu = 0x3F;
00986 }
00987
00988 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
00989 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
00990 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
00991 } else {
00992
00993 yl = 0;
00994 yu = 0x3F << 6;
00995 }
00996
00997 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00998 for (int x = xl;; x = (x + 1) & 0x3F) {
00999 const Vehicle *v = _vehicle_viewport_hash[x + y];
01000
01001 while (v != NULL) {
01002 if (!(v->vehstatus & VS_HIDDEN) &&
01003 l <= v->coord.right &&
01004 t <= v->coord.bottom &&
01005 r >= v->coord.left &&
01006 b >= v->coord.top) {
01007 DoDrawVehicle(v);
01008 }
01009 v = v->hash_viewport_next;
01010 }
01011
01012 if (x == xu) break;
01013 }
01014
01015 if (y == yu) break;
01016 }
01017 }
01018
01026 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01027 {
01028 Vehicle *found = NULL, *v;
01029 uint dist, best_dist = UINT_MAX;
01030
01031 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01032
01033 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01034 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01035
01036 FOR_ALL_VEHICLES(v) {
01037 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
01038 x >= v->coord.left && x <= v->coord.right &&
01039 y >= v->coord.top && y <= v->coord.bottom) {
01040
01041 dist = max(
01042 abs(((v->coord.left + v->coord.right) >> 1) - x),
01043 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01044 );
01045
01046 if (dist < best_dist) {
01047 found = v;
01048 best_dist = dist;
01049 }
01050 }
01051 }
01052
01053 return found;
01054 }
01055
01060 void DecreaseVehicleValue(Vehicle *v)
01061 {
01062 v->value -= v->value >> 8;
01063 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01064 }
01065
01066 static const byte _breakdown_chance[64] = {
01067 3, 3, 3, 3, 3, 3, 3, 3,
01068 4, 4, 5, 5, 6, 6, 7, 7,
01069 8, 8, 9, 9, 10, 10, 11, 11,
01070 12, 13, 13, 13, 13, 14, 15, 16,
01071 17, 19, 21, 25, 28, 31, 34, 37,
01072 40, 44, 48, 52, 56, 60, 64, 68,
01073 72, 80, 90, 100, 110, 120, 130, 140,
01074 150, 170, 190, 210, 230, 250, 250, 250,
01075 };
01076
01077 void CheckVehicleBreakdown(Vehicle *v)
01078 {
01079 int rel, rel_old;
01080
01081
01082 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01083 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01084
01085 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01086 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01087 v->cur_speed < 5 || _game_mode == GM_MENU) {
01088 return;
01089 }
01090
01091 uint32 r = Random();
01092
01093
01094 int chance = v->breakdown_chance + 1;
01095 if (Chance16I(1, 25, r)) chance += 25;
01096 v->breakdown_chance = min(255, chance);
01097
01098
01099 rel = v->reliability;
01100 if (v->type == VEH_SHIP) rel += 0x6666;
01101
01102
01103 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01104
01105
01106 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01107 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01108 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01109 v->breakdown_chance = 0;
01110 }
01111 }
01112
01119 bool Vehicle::HandleBreakdown()
01120 {
01121
01122
01123
01124
01125
01126 switch (this->breakdown_ctr) {
01127 case 0:
01128 return false;
01129
01130 case 2:
01131 this->breakdown_ctr = 1;
01132
01133 if (this->breakdowns_since_last_service != 255) {
01134 this->breakdowns_since_last_service++;
01135 }
01136
01137 if (this->type == VEH_AIRCRAFT) {
01138
01139 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01140 } else {
01141 this->cur_speed = 0;
01142
01143 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01144 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01145 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01146 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01147 }
01148
01149 if (!(this->vehstatus & VS_HIDDEN)) {
01150 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01151 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01152 }
01153 }
01154
01155 this->MarkDirty();
01156 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01157 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01158
01159
01160 case 1:
01161
01162 if (this->type == VEH_AIRCRAFT) return false;
01163
01164
01165 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01166 if (--this->breakdown_delay == 0) {
01167 this->breakdown_ctr = 0;
01168 this->MarkDirty();
01169 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01170 }
01171 }
01172 return true;
01173
01174 default:
01175 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01176 return false;
01177 }
01178 }
01179
01184 void AgeVehicle(Vehicle *v)
01185 {
01186 if (v->age < MAX_DAY) {
01187 v->age++;
01188 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
01189 }
01190
01191 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
01192
01193 int age = v->age - v->max_age;
01194 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01195 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01196 v->reliability_spd_dec <<= 1;
01197 }
01198
01199 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01200
01201
01202 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01203
01204
01205 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
01206
01207 StringID str;
01208 if (age == -DAYS_IN_LEAP_YEAR) {
01209 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01210 } else if (age == 0) {
01211 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01212 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01213 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01214 } else {
01215 return;
01216 }
01217
01218 SetDParam(0, v->index);
01219 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01220 }
01221
01228 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01229 {
01230 int count = 0;
01231 int max = 0;
01232 int cars = 0;
01233 int unloading = 0;
01234 bool loading = false;
01235
01236 const Vehicle *u = v;
01237
01238 const Station *st = Station::GetIfValid(v->last_station_visited);
01239 assert(colour == NULL || st != NULL);
01240
01241
01242 for (; v != NULL; v = v->Next()) {
01243 count += v->cargo.Count();
01244 max += v->cargo_cap;
01245 if (v->cargo_cap != 0 && colour != NULL) {
01246 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01247 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01248 cars++;
01249 }
01250 }
01251
01252 if (colour != NULL) {
01253 if (unloading == 0 && loading) {
01254 *colour = STR_PERCENT_UP;
01255 } else if (cars == unloading || !loading) {
01256 *colour = STR_PERCENT_DOWN;
01257 } else {
01258 *colour = STR_PERCENT_UP_DOWN;
01259 }
01260 }
01261
01262
01263 if (max == 0) return 100;
01264
01265
01266 return (count * 100) / max;
01267 }
01268
01273 void VehicleEnterDepot(Vehicle *v)
01274 {
01275
01276 assert(v == v->First());
01277
01278 switch (v->type) {
01279 case VEH_TRAIN: {
01280 Train *t = Train::From(v);
01281 SetWindowClassesDirty(WC_TRAINS_LIST);
01282
01283 SetDepotReservation(t->tile, false);
01284 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01285
01286 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01287 t->wait_counter = 0;
01288 t->force_proceed = TFP_NONE;
01289 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01290 t->ConsistChanged(true);
01291 break;
01292 }
01293
01294 case VEH_ROAD:
01295 SetWindowClassesDirty(WC_ROADVEH_LIST);
01296 break;
01297
01298 case VEH_SHIP: {
01299 SetWindowClassesDirty(WC_SHIPS_LIST);
01300 Ship *ship = Ship::From(v);
01301 ship->state = TRACK_BIT_DEPOT;
01302 ship->UpdateCache();
01303 ship->UpdateViewport(true, true);
01304 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01305 break;
01306 }
01307
01308 case VEH_AIRCRAFT:
01309 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01310 HandleAircraftEnterHangar(Aircraft::From(v));
01311 break;
01312 default: NOT_REACHED();
01313 }
01314 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01315
01316 if (v->type != VEH_TRAIN) {
01317
01318
01319 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01320 }
01321 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01322
01323 v->vehstatus |= VS_HIDDEN;
01324 v->cur_speed = 0;
01325
01326 VehicleServiceInDepot(v);
01327
01328 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01329
01330 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01331 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01332
01333 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01334 Order t = v->current_order;
01335 v->current_order.MakeDummy();
01336
01337
01338
01339 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01340 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01341 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01342
01343 return;
01344 }
01345
01346 if (t.IsRefit()) {
01347 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01348 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01349 cur_company.Restore();
01350
01351 if (cost.Failed()) {
01352 _vehicles_to_autoreplace[v] = false;
01353 if (v->owner == _local_company) {
01354
01355 SetDParam(0, v->index);
01356 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01357 }
01358 } else if (cost.GetCost() != 0) {
01359 v->profit_this_year -= cost.GetCost() << 8;
01360 if (v->owner == _local_company) {
01361 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01362 }
01363 }
01364 }
01365
01366 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01367
01368 v->DeleteUnreachedImplicitOrders();
01369 UpdateVehicleTimetable(v, true);
01370 v->IncrementImplicitOrderIndex();
01371 }
01372 if (t.GetDepotActionType() & ODATFB_HALT) {
01373
01374 _vehicles_to_autoreplace[v] = false;
01375 if (v->owner == _local_company) {
01376 SetDParam(0, v->index);
01377 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01378 }
01379 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01380 }
01381 }
01382 }
01383
01384
01390 void VehicleUpdatePosition(Vehicle *v)
01391 {
01392 UpdateVehicleTileHash(v, false);
01393 }
01394
01401 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01402 {
01403 int img = v->cur_image;
01404 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01405 const Sprite *spr = GetSprite(img, ST_NORMAL);
01406
01407 pt.x += spr->x_offs;
01408 pt.y += spr->y_offs;
01409
01410 UpdateVehicleViewportHash(v, pt.x, pt.y);
01411
01412 Rect old_coord = v->coord;
01413 v->coord.left = pt.x;
01414 v->coord.top = pt.y;
01415 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01416 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01417
01418 if (dirty) {
01419 MarkAllViewportsDirty(
01420 min(old_coord.left, v->coord.left),
01421 min(old_coord.top, v->coord.top),
01422 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01423 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01424 );
01425 }
01426 }
01427
01432 void VehicleUpdatePositionAndViewport(Vehicle *v)
01433 {
01434 VehicleUpdatePosition(v);
01435 VehicleUpdateViewport(v, true);
01436 }
01437
01442 void MarkSingleVehicleDirty(const Vehicle *v)
01443 {
01444 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01445 }
01446
01452 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01453 {
01454 static const int8 _delta_coord[16] = {
01455 -1,-1,-1, 0, 1, 1, 1, 0,
01456 -1, 0, 1, 1, 1, 0,-1,-1,
01457 };
01458
01459 int x = v->x_pos + _delta_coord[v->direction];
01460 int y = v->y_pos + _delta_coord[v->direction + 8];
01461
01462 GetNewVehiclePosResult gp;
01463 gp.x = x;
01464 gp.y = y;
01465 gp.old_tile = v->tile;
01466 gp.new_tile = TileVirtXY(x, y);
01467 return gp;
01468 }
01469
01470 static const Direction _new_direction_table[] = {
01471 DIR_N, DIR_NW, DIR_W,
01472 DIR_NE, DIR_SE, DIR_SW,
01473 DIR_E, DIR_SE, DIR_S
01474 };
01475
01476 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01477 {
01478 int i = 0;
01479
01480 if (y >= v->y_pos) {
01481 if (y != v->y_pos) i += 3;
01482 i += 3;
01483 }
01484
01485 if (x >= v->x_pos) {
01486 if (x != v->x_pos) i++;
01487 i++;
01488 }
01489
01490 Direction dir = v->direction;
01491
01492 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01493 if (dirdiff == DIRDIFF_SAME) return dir;
01494 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01495 }
01496
01506 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01507 {
01508 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01509 }
01510
01518 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01519 {
01520
01521 const Vehicle *v;
01522 FOR_ALL_VEHICLES(v) {
01523 if (v->type == type && v->owner == owner) {
01524 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01525 }
01526 }
01527
01528 if (this->maxid == 0) return;
01529
01530
01531
01532
01533 this->cache = CallocT<bool>(this->maxid + 2);
01534
01535
01536 FOR_ALL_VEHICLES(v) {
01537 if (v->type == type && v->owner == owner) {
01538 this->cache[v->unitnumber] = true;
01539 }
01540 }
01541 }
01542
01544 UnitID FreeUnitIDGenerator::NextID()
01545 {
01546 if (this->maxid <= this->curid) return ++this->curid;
01547
01548 while (this->cache[++this->curid]) { }
01549
01550 return this->curid;
01551 }
01552
01558 UnitID GetFreeUnitNumber(VehicleType type)
01559 {
01560
01561 uint max_veh;
01562 switch (type) {
01563 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01564 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01565 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01566 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01567 default: NOT_REACHED();
01568 }
01569
01570 const Company *c = Company::Get(_current_company);
01571 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01572
01573 FreeUnitIDGenerator gen(type, _current_company);
01574
01575 return gen.NextID();
01576 }
01577
01578
01587 bool CanBuildVehicleInfrastructure(VehicleType type)
01588 {
01589 assert(IsCompanyBuildableVehicleType(type));
01590
01591 if (!Company::IsValidID(_local_company)) return false;
01592 if (!_settings_client.gui.disable_unsuitable_building) return true;
01593
01594 UnitID max;
01595 switch (type) {
01596 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01597 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01598 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01599 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01600 default: NOT_REACHED();
01601 }
01602
01603
01604 if (max > 0) {
01605
01606 const Engine *e;
01607 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01608 if (HasBit(e->company_avail, _local_company)) return true;
01609 }
01610 return false;
01611 }
01612
01613
01614 const Vehicle *v;
01615 FOR_ALL_VEHICLES(v) {
01616 if (v->owner == _local_company && v->type == type) return true;
01617 }
01618
01619 return false;
01620 }
01621
01622
01630 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01631 {
01632 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01633 const Engine *e = Engine::Get(engine_type);
01634 switch (e->type) {
01635 default: NOT_REACHED();
01636 case VEH_TRAIN:
01637 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01638
01639
01640 engine_type = parent_engine_type;
01641 e = Engine::Get(engine_type);
01642
01643 }
01644
01645 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01646 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01647 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01648 if (!CargoSpec::Get(cargo_type)->is_freight) {
01649 if (parent_engine_type == INVALID_ENGINE) {
01650 return LS_PASSENGER_WAGON_STEAM;
01651 } else {
01652 switch (RailVehInfo(parent_engine_type)->engclass) {
01653 default: NOT_REACHED();
01654 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01655 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01656 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01657 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01658 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01659 }
01660 }
01661 } else {
01662 return LS_FREIGHT_WAGON;
01663 }
01664 } else {
01665 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01666
01667 switch (e->u.rail.engclass) {
01668 default: NOT_REACHED();
01669 case EC_STEAM: return LS_STEAM;
01670 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01671 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01672 case EC_MONORAIL: return LS_MONORAIL;
01673 case EC_MAGLEV: return LS_MAGLEV;
01674 }
01675 }
01676
01677 case VEH_ROAD:
01678
01679 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01680 engine_type = parent_engine_type;
01681 e = Engine::Get(engine_type);
01682 cargo_type = v->First()->cargo_type;
01683 }
01684 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01685 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01686
01687
01688 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01689
01690 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01691 } else {
01692
01693 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01694 }
01695
01696 case VEH_SHIP:
01697 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01698 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01699 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01700
01701 case VEH_AIRCRAFT:
01702 switch (e->u.air.subtype) {
01703 case AIR_HELI: return LS_HELICOPTER;
01704 case AIR_CTOL: return LS_SMALL_PLANE;
01705 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01706 default: NOT_REACHED();
01707 }
01708 }
01709 }
01710
01720 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01721 {
01722 const Company *c = Company::Get(company);
01723 LiveryScheme scheme = LS_DEFAULT;
01724
01725
01726
01727 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01728
01729 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01730
01731
01732 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01733 }
01734
01735 return &c->livery[scheme];
01736 }
01737
01738
01739 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01740 {
01741 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01742
01743
01744 if (map != PAL_NONE) return map;
01745
01746 const Engine *e = Engine::Get(engine_type);
01747
01748
01749 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01750 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01751
01752 if (callback != CALLBACK_FAILED) {
01753 assert_compile(PAL_NONE == 0);
01754 map = GB(callback, 0, 14);
01755
01756
01757 if (!HasBit(callback, 14)) {
01758
01759 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01760 return map;
01761 }
01762 }
01763 }
01764
01765 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01766
01767 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01768
01769
01770 if (!Company::IsValidID(company)) return map;
01771
01772 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01773
01774 map += livery->colour1;
01775 if (twocc) map += livery->colour2 * 16;
01776
01777
01778 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01779 return map;
01780 }
01781
01788 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01789 {
01790 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01791 }
01792
01798 PaletteID GetVehiclePalette(const Vehicle *v)
01799 {
01800 if (v->IsGroundVehicle()) {
01801 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01802 }
01803
01804 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01805 }
01806
01810 void Vehicle::DeleteUnreachedImplicitOrders()
01811 {
01812 if (this->IsGroundVehicle()) {
01813 uint16 &gv_flags = this->GetGroundVehicleFlags();
01814 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01815
01816 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01817 this->cur_implicit_order_index = this->cur_real_order_index;
01818 InvalidateVehicleOrder(this, 0);
01819 return;
01820 }
01821 }
01822
01823 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01824 while (order != NULL) {
01825 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01826
01827 if (order->IsType(OT_IMPLICIT)) {
01828
01829 order = order->next;
01830 DeleteOrder(this, this->cur_implicit_order_index);
01831 } else {
01832
01833 order = order->next;
01834 this->cur_implicit_order_index++;
01835 }
01836
01837
01838 if (order == NULL) {
01839 order = this->GetOrder(0);
01840 this->cur_implicit_order_index = 0;
01841 }
01842 }
01843 }
01844
01849 void Vehicle::BeginLoading()
01850 {
01851 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01852
01853 if (this->current_order.IsType(OT_GOTO_STATION) &&
01854 this->current_order.GetDestination() == this->last_station_visited) {
01855 this->DeleteUnreachedImplicitOrders();
01856
01857
01858 this->current_order.MakeLoading(true);
01859 UpdateVehicleTimetable(this, true);
01860
01861
01862
01863
01864
01865
01866 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01867
01868 } else {
01869
01870
01871
01872
01873
01874 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01875 if (this->IsGroundVehicle() && in_list != NULL &&
01876 (!in_list->IsType(OT_IMPLICIT) ||
01877 in_list->GetDestination() != this->last_station_visited)) {
01878 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01879
01880 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01881 if (prev_order == NULL ||
01882 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01883 prev_order->GetDestination() != this->last_station_visited) {
01884
01885
01886
01887 int target_index = this->cur_implicit_order_index;
01888 bool found = false;
01889 while (target_index != this->cur_real_order_index) {
01890 const Order *order = this->GetOrder(target_index);
01891 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01892 found = true;
01893 break;
01894 }
01895 target_index++;
01896 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01897 assert(target_index != this->cur_implicit_order_index);
01898 }
01899
01900 if (found) {
01901 if (suppress_implicit_orders) {
01902
01903 this->cur_implicit_order_index = target_index;
01904 InvalidateVehicleOrder(this, 0);
01905 } else {
01906
01907 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01908 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01909 if (order->IsType(OT_IMPLICIT)) {
01910
01911 order = order->next;
01912 DeleteOrder(this, this->cur_implicit_order_index);
01913 } else {
01914
01915 order = order->next;
01916 this->cur_implicit_order_index++;
01917 }
01918
01919
01920 if (order == NULL) {
01921 order = this->GetOrder(0);
01922 this->cur_implicit_order_index = 0;
01923 }
01924 assert(order != NULL);
01925 }
01926 }
01927 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01928
01929 Order *implicit_order = new Order();
01930 implicit_order->MakeImplicit(this->last_station_visited);
01931 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01932 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01933
01934
01935
01936 uint16 &gv_flags = this->GetGroundVehicleFlags();
01937 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01938 }
01939 }
01940 }
01941 this->current_order.MakeLoading(false);
01942 }
01943
01944 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01945
01946 PrepareUnload(this);
01947
01948 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01949 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
01950 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01951 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01952
01953 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01954 this->cur_speed = 0;
01955 this->MarkDirty();
01956 }
01957
01962 void Vehicle::LeaveStation()
01963 {
01964 assert(this->current_order.IsType(OT_LOADING));
01965
01966 delete this->cargo_payment;
01967
01968
01969 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01970
01971 this->current_order.MakeLeaveStation();
01972 Station *st = Station::Get(this->last_station_visited);
01973 st->loading_vehicles.remove(this);
01974
01975 HideFillingPercent(&this->fill_percent_te_id);
01976
01977 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01978
01979 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
01980
01981 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
01982 }
01983 }
01984
01985
01991 void Vehicle::HandleLoading(bool mode)
01992 {
01993 switch (this->current_order.GetType()) {
01994 case OT_LOADING: {
01995 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01996
01997
01998 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
01999
02000 this->PlayLeaveStationSound();
02001
02002 this->LeaveStation();
02003
02004
02005 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02006 if (order == NULL ||
02007 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02008 order->GetDestination() != this->last_station_visited) {
02009 return;
02010 }
02011 break;
02012 }
02013
02014 case OT_DUMMY: break;
02015
02016 default: return;
02017 }
02018
02019 this->IncrementImplicitOrderIndex();
02020 }
02021
02028 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02029 {
02030 CommandCost ret = CheckOwnership(this->owner);
02031 if (ret.Failed()) return ret;
02032
02033 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02034 if (this->IsStoppedInDepot()) return CMD_ERROR;
02035
02036 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02037 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02038 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02039
02040
02041
02042 if (flags & DC_EXEC) {
02043 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02044 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02045 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02046 }
02047 return CommandCost();
02048 }
02049
02050 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02051 if (flags & DC_EXEC) {
02052
02053
02054 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02055
02056 if (this->IsGroundVehicle()) {
02057 uint16 &gv_flags = this->GetGroundVehicleFlags();
02058 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02059 }
02060
02061 this->current_order.MakeDummy();
02062 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02063 }
02064 return CommandCost();
02065 }
02066
02067 TileIndex location;
02068 DestinationID destination;
02069 bool reverse;
02070 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};
02071 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02072
02073 if (flags & DC_EXEC) {
02074 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02075
02076 if (this->IsGroundVehicle()) {
02077 uint16 &gv_flags = this->GetGroundVehicleFlags();
02078 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02079 }
02080
02081 this->dest_tile = location;
02082 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02083 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02084 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02085
02086
02087 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02088
02089 if (this->type == VEH_AIRCRAFT) {
02090 Aircraft *a = Aircraft::From(this);
02091 if (a->state == FLYING && a->targetairport != destination) {
02092
02093 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02094 AircraftNextAirportPos_and_Order(a);
02095 }
02096 }
02097 }
02098
02099 return CommandCost();
02100
02101 }
02102
02107 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02108 {
02109 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02110 const Engine *e = this->GetEngine();
02111
02112
02113 byte visual_effect;
02114 switch (e->type) {
02115 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02116 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02117 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02118 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02119 }
02120
02121
02122 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02123 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02124
02125 if (callback != CALLBACK_FAILED) {
02126 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02127
02128 callback = GB(callback, 0, 8);
02129
02130
02131 if (callback == VE_DEFAULT) {
02132 assert(HasBit(callback, VE_DISABLE_EFFECT));
02133 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02134 }
02135 visual_effect = callback;
02136 }
02137 }
02138
02139
02140 if (visual_effect == VE_DEFAULT ||
02141 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02142
02143
02144 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02145 if (visual_effect == VE_DEFAULT) {
02146 visual_effect = 1 << VE_DISABLE_EFFECT;
02147 } else {
02148 SetBit(visual_effect, VE_DISABLE_EFFECT);
02149 }
02150 } else {
02151 if (visual_effect == VE_DEFAULT) {
02152
02153 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02154 }
02155 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02156 }
02157 }
02158
02159 this->vcache.cached_vis_effect = visual_effect;
02160
02161 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02162 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02163 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02164 }
02165 }
02166
02167 static const int8 _vehicle_smoke_pos[8] = {
02168 1, 1, 1, 0, -1, -1, -1, 0
02169 };
02170
02175 void Vehicle::ShowVisualEffect() const
02176 {
02177 assert(this->IsPrimaryVehicle());
02178 bool sound = false;
02179
02180
02181
02182
02183
02184
02185 if (_settings_game.vehicle.smoke_amount == 0 ||
02186 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02187 this->cur_speed < 2) {
02188 return;
02189 }
02190
02191 uint max_speed = this->vcache.cached_max_speed;
02192 if (this->type == VEH_TRAIN) {
02193 const Train *t = Train::From(this);
02194
02195
02196
02197
02198 if (HasBit(t->flags, VRF_REVERSING) ||
02199 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02200 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02201 return;
02202 }
02203
02204 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02205 max_speed = min(max_speed, this->current_order.max_speed);
02206 }
02207 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02208
02209 const Vehicle *v = this;
02210
02211 do {
02212 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02213 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02214 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02215
02216
02217
02218
02219
02220
02221
02222
02223 if (disable_effect ||
02224 v->vehstatus & VS_HIDDEN ||
02225 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02226 IsDepotTile(v->tile) ||
02227 IsTunnelTile(v->tile) ||
02228 (v->type == VEH_TRAIN &&
02229 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02230 continue;
02231 }
02232
02233
02234
02235
02236 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02237
02238 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02239 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02240
02241 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02242 x = -x;
02243 y = -y;
02244 }
02245
02246 switch (effect_type) {
02247 case VE_TYPE_STEAM:
02248
02249
02250
02251
02252
02253 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02254 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02255 sound = true;
02256 }
02257 break;
02258
02259 case VE_TYPE_DIESEL: {
02260
02261
02262
02263
02264
02265
02266
02267
02268
02269
02270
02271 int power_weight_effect = 0;
02272 if (v->type == VEH_TRAIN) {
02273 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02274 }
02275 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02276 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02277 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02278 sound = true;
02279 }
02280 break;
02281 }
02282
02283 case VE_TYPE_ELECTRIC:
02284
02285
02286
02287
02288
02289
02290 if (GB(v->tick_counter, 0, 2) == 0 &&
02291 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02292 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02293 sound = true;
02294 }
02295 break;
02296
02297 default:
02298 break;
02299 }
02300 } while ((v = v->Next()) != NULL);
02301
02302 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02303 }
02304
02309 void Vehicle::SetNext(Vehicle *next)
02310 {
02311 assert(this != next);
02312
02313 if (this->next != NULL) {
02314
02315 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02316 v->first = this->next;
02317 }
02318 this->next->previous = NULL;
02319 }
02320
02321 this->next = next;
02322
02323 if (this->next != NULL) {
02324
02325 if (this->next->previous != NULL) this->next->previous->next = NULL;
02326 this->next->previous = this;
02327 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02328 v->first = this->first;
02329 }
02330 }
02331 }
02332
02338 void Vehicle::AddToShared(Vehicle *shared_chain)
02339 {
02340 assert(this->previous_shared == NULL && this->next_shared == NULL);
02341
02342 if (shared_chain->orders.list == NULL) {
02343 assert(shared_chain->previous_shared == NULL);
02344 assert(shared_chain->next_shared == NULL);
02345 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02346 }
02347
02348 this->next_shared = shared_chain->next_shared;
02349 this->previous_shared = shared_chain;
02350
02351 shared_chain->next_shared = this;
02352
02353 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02354
02355 shared_chain->orders.list->AddVehicle(this);
02356 }
02357
02361 void Vehicle::RemoveFromShared()
02362 {
02363
02364
02365 bool were_first = (this->FirstShared() == this);
02366 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02367
02368 this->orders.list->RemoveVehicle(this);
02369
02370 if (!were_first) {
02371
02372 this->previous_shared->next_shared = this->NextShared();
02373 }
02374
02375 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02376
02377
02378 if (this->orders.list->GetNumVehicles() == 1) {
02379
02380 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02381 InvalidateVehicleOrder(this->FirstShared(), 0);
02382 } else if (were_first) {
02383
02384
02385 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02386 }
02387
02388 this->next_shared = NULL;
02389 this->previous_shared = NULL;
02390 }
02391
02392 void VehiclesYearlyLoop()
02393 {
02394 Vehicle *v;
02395 FOR_ALL_VEHICLES(v) {
02396 if (v->IsPrimaryVehicle()) {
02397
02398 Money profit = v->GetDisplayProfitThisYear();
02399 if (v->age >= 730 && profit < 0) {
02400 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02401 SetDParam(0, v->index);
02402 SetDParam(1, profit);
02403 AddVehicleNewsItem(
02404 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02405 NS_ADVICE,
02406 v->index
02407 );
02408 }
02409 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02410 }
02411
02412 v->profit_last_year = v->profit_this_year;
02413 v->profit_this_year = 0;
02414 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02415 }
02416 }
02417 GroupStatistics::UpdateProfits();
02418 SetWindowClassesDirty(WC_TRAINS_LIST);
02419 SetWindowClassesDirty(WC_SHIPS_LIST);
02420 SetWindowClassesDirty(WC_ROADVEH_LIST);
02421 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02422 }
02423
02424
02434 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02435 {
02436 const Engine *e = Engine::GetIfValid(engine_type);
02437 assert(e != NULL);
02438
02439 switch (e->type) {
02440 case VEH_TRAIN:
02441 return (st->facilities & FACIL_TRAIN) != 0;
02442
02443 case VEH_ROAD:
02444
02445
02446
02447 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02448
02449 case VEH_SHIP:
02450 return (st->facilities & FACIL_DOCK) != 0;
02451
02452 case VEH_AIRCRAFT:
02453 return (st->facilities & FACIL_AIRPORT) != 0 &&
02454 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02455
02456 default:
02457 return false;
02458 }
02459 }
02460
02467 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02468 {
02469 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02470
02471 return CanVehicleUseStation(v->engine_type, st);
02472 }
02473
02479 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02480 {
02481 assert(this->IsGroundVehicle());
02482 if (this->type == VEH_TRAIN) {
02483 return &Train::From(this)->gcache;
02484 } else {
02485 return &RoadVehicle::From(this)->gcache;
02486 }
02487 }
02488
02494 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02495 {
02496 assert(this->IsGroundVehicle());
02497 if (this->type == VEH_TRAIN) {
02498 return &Train::From(this)->gcache;
02499 } else {
02500 return &RoadVehicle::From(this)->gcache;
02501 }
02502 }
02503
02509 uint16 &Vehicle::GetGroundVehicleFlags()
02510 {
02511 assert(this->IsGroundVehicle());
02512 if (this->type == VEH_TRAIN) {
02513 return Train::From(this)->gv_flags;
02514 } else {
02515 return RoadVehicle::From(this)->gv_flags;
02516 }
02517 }
02518
02524 const uint16 &Vehicle::GetGroundVehicleFlags() const
02525 {
02526 assert(this->IsGroundVehicle());
02527 if (this->type == VEH_TRAIN) {
02528 return Train::From(this)->gv_flags;
02529 } else {
02530 return RoadVehicle::From(this)->gv_flags;
02531 }
02532 }
02533
02542 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02543 {
02544 if (v->type == VEH_TRAIN) {
02545 Train *u = Train::From(v);
02546
02547 u = u->GetFirstEnginePart();
02548
02549
02550 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02551 do {
02552
02553 set.Include(u->index);
02554
02555
02556 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02557
02558 u = u->Next();
02559 } while (u != NULL && u->IsArticulatedPart());
02560 }
02561 }
02562 }