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