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