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 *front, 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 bool is_loading = front->current_order.IsType(OT_LOADING);
01244
01245
01246 const Station *st = Station::GetIfValid(front->last_station_visited);
01247 assert(colour == NULL || (st != NULL && is_loading));
01248
01249 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
01250 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
01251
01252
01253 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
01254 count += v->cargo.Count();
01255 max += v->cargo_cap;
01256 if (v->cargo_cap != 0 && colour != NULL) {
01257 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01258 loading |= !order_no_load &&
01259 (order_full_load || HasBit(st->goods[v->cargo_type].acceptance_pickup, GoodsEntry::GES_PICKUP)) &&
01260 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
01261 cars++;
01262 }
01263 }
01264
01265 if (colour != NULL) {
01266 if (unloading == 0 && loading) {
01267 *colour = STR_PERCENT_UP;
01268 } else if (unloading == 0 && !loading) {
01269 *colour = STR_PERCENT_NONE;
01270 } else if (cars == unloading || !loading) {
01271 *colour = STR_PERCENT_DOWN;
01272 } else {
01273 *colour = STR_PERCENT_UP_DOWN;
01274 }
01275 }
01276
01277
01278 if (max == 0) return 100;
01279
01280
01281 return (count * 100) / max;
01282 }
01283
01288 void VehicleEnterDepot(Vehicle *v)
01289 {
01290
01291 assert(v == v->First());
01292
01293 switch (v->type) {
01294 case VEH_TRAIN: {
01295 Train *t = Train::From(v);
01296 SetWindowClassesDirty(WC_TRAINS_LIST);
01297
01298 SetDepotReservation(t->tile, false);
01299 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01300
01301 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01302 t->wait_counter = 0;
01303 t->force_proceed = TFP_NONE;
01304 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01305 t->ConsistChanged(true);
01306 break;
01307 }
01308
01309 case VEH_ROAD:
01310 SetWindowClassesDirty(WC_ROADVEH_LIST);
01311 break;
01312
01313 case VEH_SHIP: {
01314 SetWindowClassesDirty(WC_SHIPS_LIST);
01315 Ship *ship = Ship::From(v);
01316 ship->state = TRACK_BIT_DEPOT;
01317 ship->UpdateCache();
01318 ship->UpdateViewport(true, true);
01319 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01320 break;
01321 }
01322
01323 case VEH_AIRCRAFT:
01324 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01325 HandleAircraftEnterHangar(Aircraft::From(v));
01326 break;
01327 default: NOT_REACHED();
01328 }
01329 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01330
01331 if (v->type != VEH_TRAIN) {
01332
01333
01334 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01335 }
01336 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01337
01338 v->vehstatus |= VS_HIDDEN;
01339 v->cur_speed = 0;
01340
01341 VehicleServiceInDepot(v);
01342
01343 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01344
01345 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01346 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01347
01348 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01349 Order t = v->current_order;
01350 v->current_order.MakeDummy();
01351
01352
01353
01354 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01355 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01356 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01357
01358 return;
01359 }
01360
01361 if (t.IsRefit()) {
01362 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01363 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01364 cur_company.Restore();
01365
01366 if (cost.Failed()) {
01367 _vehicles_to_autoreplace[v] = false;
01368 if (v->owner == _local_company) {
01369
01370 SetDParam(0, v->index);
01371 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
01372 }
01373 } else if (cost.GetCost() != 0) {
01374 v->profit_this_year -= cost.GetCost() << 8;
01375 if (v->owner == _local_company) {
01376 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01377 }
01378 }
01379 }
01380
01381 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01382
01383 v->DeleteUnreachedImplicitOrders();
01384 UpdateVehicleTimetable(v, true);
01385 v->IncrementImplicitOrderIndex();
01386 }
01387 if (t.GetDepotActionType() & ODATFB_HALT) {
01388
01389 _vehicles_to_autoreplace[v] = false;
01390 if (v->owner == _local_company) {
01391 SetDParam(0, v->index);
01392 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
01393 }
01394 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01395 }
01396 }
01397 }
01398
01399
01405 void VehicleUpdatePosition(Vehicle *v)
01406 {
01407 UpdateVehicleTileHash(v, false);
01408 }
01409
01416 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01417 {
01418 int img = v->cur_image;
01419 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01420 const Sprite *spr = GetSprite(img, ST_NORMAL);
01421
01422 pt.x += spr->x_offs;
01423 pt.y += spr->y_offs;
01424
01425 UpdateVehicleViewportHash(v, pt.x, pt.y);
01426
01427 Rect old_coord = v->coord;
01428 v->coord.left = pt.x;
01429 v->coord.top = pt.y;
01430 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01431 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01432
01433 if (dirty) {
01434 if (old_coord.left == INVALID_COORD) {
01435 MarkSingleVehicleDirty(v);
01436 } else {
01437 MarkAllViewportsDirty(
01438 min(old_coord.left, v->coord.left),
01439 min(old_coord.top, v->coord.top),
01440 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01441 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01442 );
01443 }
01444 }
01445 }
01446
01451 void VehicleUpdatePositionAndViewport(Vehicle *v)
01452 {
01453 VehicleUpdatePosition(v);
01454 VehicleUpdateViewport(v, true);
01455 }
01456
01461 void MarkSingleVehicleDirty(const Vehicle *v)
01462 {
01463 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01464 }
01465
01471 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01472 {
01473 static const int8 _delta_coord[16] = {
01474 -1,-1,-1, 0, 1, 1, 1, 0,
01475 -1, 0, 1, 1, 1, 0,-1,-1,
01476 };
01477
01478 int x = v->x_pos + _delta_coord[v->direction];
01479 int y = v->y_pos + _delta_coord[v->direction + 8];
01480
01481 GetNewVehiclePosResult gp;
01482 gp.x = x;
01483 gp.y = y;
01484 gp.old_tile = v->tile;
01485 gp.new_tile = TileVirtXY(x, y);
01486 return gp;
01487 }
01488
01489 static const Direction _new_direction_table[] = {
01490 DIR_N, DIR_NW, DIR_W,
01491 DIR_NE, DIR_SE, DIR_SW,
01492 DIR_E, DIR_SE, DIR_S
01493 };
01494
01495 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01496 {
01497 int i = 0;
01498
01499 if (y >= v->y_pos) {
01500 if (y != v->y_pos) i += 3;
01501 i += 3;
01502 }
01503
01504 if (x >= v->x_pos) {
01505 if (x != v->x_pos) i++;
01506 i++;
01507 }
01508
01509 Direction dir = v->direction;
01510
01511 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01512 if (dirdiff == DIRDIFF_SAME) return dir;
01513 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01514 }
01515
01525 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01526 {
01527 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01528 }
01529
01537 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01538 {
01539
01540 const Vehicle *v;
01541 FOR_ALL_VEHICLES(v) {
01542 if (v->type == type && v->owner == owner) {
01543 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01544 }
01545 }
01546
01547 if (this->maxid == 0) return;
01548
01549
01550
01551
01552 this->cache = CallocT<bool>(this->maxid + 2);
01553
01554
01555 FOR_ALL_VEHICLES(v) {
01556 if (v->type == type && v->owner == owner) {
01557 this->cache[v->unitnumber] = true;
01558 }
01559 }
01560 }
01561
01563 UnitID FreeUnitIDGenerator::NextID()
01564 {
01565 if (this->maxid <= this->curid) return ++this->curid;
01566
01567 while (this->cache[++this->curid]) { }
01568
01569 return this->curid;
01570 }
01571
01577 UnitID GetFreeUnitNumber(VehicleType type)
01578 {
01579
01580 uint max_veh;
01581 switch (type) {
01582 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01583 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01584 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01585 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01586 default: NOT_REACHED();
01587 }
01588
01589 const Company *c = Company::Get(_current_company);
01590 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01591
01592 FreeUnitIDGenerator gen(type, _current_company);
01593
01594 return gen.NextID();
01595 }
01596
01597
01606 bool CanBuildVehicleInfrastructure(VehicleType type)
01607 {
01608 assert(IsCompanyBuildableVehicleType(type));
01609
01610 if (!Company::IsValidID(_local_company)) return false;
01611 if (!_settings_client.gui.disable_unsuitable_building) return true;
01612
01613 UnitID max;
01614 switch (type) {
01615 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01616 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01617 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01618 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01619 default: NOT_REACHED();
01620 }
01621
01622
01623 if (max > 0) {
01624
01625 const Engine *e;
01626 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01627 if (HasBit(e->company_avail, _local_company)) return true;
01628 }
01629 return false;
01630 }
01631
01632
01633 const Vehicle *v;
01634 FOR_ALL_VEHICLES(v) {
01635 if (v->owner == _local_company && v->type == type) return true;
01636 }
01637
01638 return false;
01639 }
01640
01641
01649 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01650 {
01651 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01652 const Engine *e = Engine::Get(engine_type);
01653 switch (e->type) {
01654 default: NOT_REACHED();
01655 case VEH_TRAIN:
01656 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01657
01658
01659 engine_type = parent_engine_type;
01660 e = Engine::Get(engine_type);
01661
01662 }
01663
01664 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01665 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01666 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01667 if (!CargoSpec::Get(cargo_type)->is_freight) {
01668 if (parent_engine_type == INVALID_ENGINE) {
01669 return LS_PASSENGER_WAGON_STEAM;
01670 } else {
01671 switch (RailVehInfo(parent_engine_type)->engclass) {
01672 default: NOT_REACHED();
01673 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01674 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01675 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01676 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01677 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01678 }
01679 }
01680 } else {
01681 return LS_FREIGHT_WAGON;
01682 }
01683 } else {
01684 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01685
01686 switch (e->u.rail.engclass) {
01687 default: NOT_REACHED();
01688 case EC_STEAM: return LS_STEAM;
01689 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01690 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01691 case EC_MONORAIL: return LS_MONORAIL;
01692 case EC_MAGLEV: return LS_MAGLEV;
01693 }
01694 }
01695
01696 case VEH_ROAD:
01697
01698 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01699 engine_type = parent_engine_type;
01700 e = Engine::Get(engine_type);
01701 cargo_type = v->First()->cargo_type;
01702 }
01703 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01704 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01705
01706
01707 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01708
01709 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01710 } else {
01711
01712 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01713 }
01714
01715 case VEH_SHIP:
01716 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01717 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01718 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01719
01720 case VEH_AIRCRAFT:
01721 switch (e->u.air.subtype) {
01722 case AIR_HELI: return LS_HELICOPTER;
01723 case AIR_CTOL: return LS_SMALL_PLANE;
01724 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01725 default: NOT_REACHED();
01726 }
01727 }
01728 }
01729
01739 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01740 {
01741 const Company *c = Company::Get(company);
01742 LiveryScheme scheme = LS_DEFAULT;
01743
01744
01745
01746 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01747
01748 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01749
01750
01751 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01752 }
01753
01754 return &c->livery[scheme];
01755 }
01756
01757
01758 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01759 {
01760 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01761
01762
01763 if (map != PAL_NONE) return map;
01764
01765 const Engine *e = Engine::Get(engine_type);
01766
01767
01768 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01769 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01770
01771 if (callback != CALLBACK_FAILED) {
01772 assert_compile(PAL_NONE == 0);
01773 map = GB(callback, 0, 14);
01774
01775
01776 if (!HasBit(callback, 14)) {
01777
01778 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01779 return map;
01780 }
01781 }
01782 }
01783
01784 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01785
01786 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01787
01788
01789 if (!Company::IsValidID(company)) return map;
01790
01791 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01792
01793 map += livery->colour1;
01794 if (twocc) map += livery->colour2 * 16;
01795
01796
01797 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01798 return map;
01799 }
01800
01807 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01808 {
01809 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01810 }
01811
01817 PaletteID GetVehiclePalette(const Vehicle *v)
01818 {
01819 if (v->IsGroundVehicle()) {
01820 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01821 }
01822
01823 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01824 }
01825
01829 void Vehicle::DeleteUnreachedImplicitOrders()
01830 {
01831 if (this->IsGroundVehicle()) {
01832 uint16 &gv_flags = this->GetGroundVehicleFlags();
01833 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01834
01835 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01836 this->cur_implicit_order_index = this->cur_real_order_index;
01837 InvalidateVehicleOrder(this, 0);
01838 return;
01839 }
01840 }
01841
01842 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01843 while (order != NULL) {
01844 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01845
01846 if (order->IsType(OT_IMPLICIT)) {
01847
01848 order = order->next;
01849 DeleteOrder(this, this->cur_implicit_order_index);
01850 } else {
01851
01852 order = order->next;
01853 this->cur_implicit_order_index++;
01854 }
01855
01856
01857 if (order == NULL) {
01858 order = this->GetOrder(0);
01859 this->cur_implicit_order_index = 0;
01860 }
01861 }
01862 }
01863
01868 void Vehicle::BeginLoading()
01869 {
01870 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01871
01872 if (this->current_order.IsType(OT_GOTO_STATION) &&
01873 this->current_order.GetDestination() == this->last_station_visited) {
01874 this->DeleteUnreachedImplicitOrders();
01875
01876
01877 this->current_order.MakeLoading(true);
01878 UpdateVehicleTimetable(this, true);
01879
01880
01881
01882
01883
01884
01885 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01886
01887 } else {
01888
01889
01890
01891
01892
01893 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01894 if (this->IsGroundVehicle() && in_list != NULL &&
01895 (!in_list->IsType(OT_IMPLICIT) ||
01896 in_list->GetDestination() != this->last_station_visited)) {
01897 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01898
01899 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01900 if (prev_order == NULL ||
01901 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01902 prev_order->GetDestination() != this->last_station_visited) {
01903
01904
01905
01906 int target_index = this->cur_implicit_order_index;
01907 bool found = false;
01908 while (target_index != this->cur_real_order_index) {
01909 const Order *order = this->GetOrder(target_index);
01910 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01911 found = true;
01912 break;
01913 }
01914 target_index++;
01915 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01916 assert(target_index != this->cur_implicit_order_index);
01917 }
01918
01919 if (found) {
01920 if (suppress_implicit_orders) {
01921
01922 this->cur_implicit_order_index = target_index;
01923 InvalidateVehicleOrder(this, 0);
01924 } else {
01925
01926 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01927 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01928 if (order->IsType(OT_IMPLICIT)) {
01929
01930 order = order->next;
01931 DeleteOrder(this, this->cur_implicit_order_index);
01932 } else {
01933
01934 order = order->next;
01935 this->cur_implicit_order_index++;
01936 }
01937
01938
01939 if (order == NULL) {
01940 order = this->GetOrder(0);
01941 this->cur_implicit_order_index = 0;
01942 }
01943 assert(order != NULL);
01944 }
01945 }
01946 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01947
01948 Order *implicit_order = new Order();
01949 implicit_order->MakeImplicit(this->last_station_visited);
01950 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01951 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01952
01953
01954
01955 uint16 &gv_flags = this->GetGroundVehicleFlags();
01956 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01957 }
01958 }
01959 }
01960 this->current_order.MakeLoading(false);
01961 }
01962
01963 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01964
01965 PrepareUnload(this);
01966
01967 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01968 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
01969 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01970 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01971
01972 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01973 this->cur_speed = 0;
01974 this->MarkDirty();
01975 }
01976
01981 void Vehicle::LeaveStation()
01982 {
01983 assert(this->current_order.IsType(OT_LOADING));
01984
01985 delete this->cargo_payment;
01986
01987
01988 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01989
01990 this->current_order.MakeLeaveStation();
01991 Station *st = Station::Get(this->last_station_visited);
01992 st->loading_vehicles.remove(this);
01993
01994 HideFillingPercent(&this->fill_percent_te_id);
01995
01996 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01997
01998 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
01999
02000 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02001 }
02002 }
02003
02004
02010 void Vehicle::HandleLoading(bool mode)
02011 {
02012 switch (this->current_order.GetType()) {
02013 case OT_LOADING: {
02014 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02015
02016
02017 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02018
02019 this->PlayLeaveStationSound();
02020
02021 this->LeaveStation();
02022
02023
02024 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02025 if (order == NULL ||
02026 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02027 order->GetDestination() != this->last_station_visited) {
02028 return;
02029 }
02030 break;
02031 }
02032
02033 case OT_DUMMY: break;
02034
02035 default: return;
02036 }
02037
02038 this->IncrementImplicitOrderIndex();
02039 }
02040
02047 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02048 {
02049 CommandCost ret = CheckOwnership(this->owner);
02050 if (ret.Failed()) return ret;
02051
02052 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02053 if (this->IsStoppedInDepot()) return CMD_ERROR;
02054
02055 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02056 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02057 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02058
02059
02060
02061 if (flags & DC_EXEC) {
02062 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02063 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02064 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02065 }
02066 return CommandCost();
02067 }
02068
02069 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02070 if (flags & DC_EXEC) {
02071
02072
02073 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02074
02075 if (this->IsGroundVehicle()) {
02076 uint16 &gv_flags = this->GetGroundVehicleFlags();
02077 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02078 }
02079
02080 this->current_order.MakeDummy();
02081 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02082 }
02083 return CommandCost();
02084 }
02085
02086 TileIndex location;
02087 DestinationID destination;
02088 bool reverse;
02089 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};
02090 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02091
02092 if (flags & DC_EXEC) {
02093 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02094
02095 if (this->IsGroundVehicle()) {
02096 uint16 &gv_flags = this->GetGroundVehicleFlags();
02097 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02098 }
02099
02100 this->dest_tile = location;
02101 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02102 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02103 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02104
02105
02106 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02107
02108 if (this->type == VEH_AIRCRAFT) {
02109 Aircraft *a = Aircraft::From(this);
02110 if (a->state == FLYING && a->targetairport != destination) {
02111
02112 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02113 AircraftNextAirportPos_and_Order(a);
02114 }
02115 }
02116 }
02117
02118 return CommandCost();
02119
02120 }
02121
02126 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02127 {
02128 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02129 const Engine *e = this->GetEngine();
02130
02131
02132 byte visual_effect;
02133 switch (e->type) {
02134 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02135 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02136 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02137 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02138 }
02139
02140
02141 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02142 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02143
02144 if (callback != CALLBACK_FAILED) {
02145 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02146
02147 callback = GB(callback, 0, 8);
02148
02149
02150 if (callback == VE_DEFAULT) {
02151 assert(HasBit(callback, VE_DISABLE_EFFECT));
02152 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02153 }
02154 visual_effect = callback;
02155 }
02156 }
02157
02158
02159 if (visual_effect == VE_DEFAULT ||
02160 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02161
02162
02163 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02164 if (visual_effect == VE_DEFAULT) {
02165 visual_effect = 1 << VE_DISABLE_EFFECT;
02166 } else {
02167 SetBit(visual_effect, VE_DISABLE_EFFECT);
02168 }
02169 } else {
02170 if (visual_effect == VE_DEFAULT) {
02171
02172 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02173 }
02174 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02175 }
02176 }
02177
02178 this->vcache.cached_vis_effect = visual_effect;
02179
02180 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02181 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02182 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02183 }
02184 }
02185
02186 static const int8 _vehicle_smoke_pos[8] = {
02187 1, 1, 1, 0, -1, -1, -1, 0
02188 };
02189
02194 void Vehicle::ShowVisualEffect() const
02195 {
02196 assert(this->IsPrimaryVehicle());
02197 bool sound = false;
02198
02199
02200
02201
02202
02203
02204 if (_settings_game.vehicle.smoke_amount == 0 ||
02205 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02206 this->cur_speed < 2) {
02207 return;
02208 }
02209
02210 uint max_speed = this->vcache.cached_max_speed;
02211 if (this->type == VEH_TRAIN) {
02212 const Train *t = Train::From(this);
02213
02214
02215
02216
02217 if (HasBit(t->flags, VRF_REVERSING) ||
02218 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02219 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02220 return;
02221 }
02222
02223 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02224 max_speed = min(max_speed, this->current_order.max_speed);
02225 }
02226 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02227
02228 const Vehicle *v = this;
02229
02230 do {
02231 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02232 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02233 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02234
02235
02236
02237
02238
02239
02240
02241
02242 if (disable_effect ||
02243 v->vehstatus & VS_HIDDEN ||
02244 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02245 IsDepotTile(v->tile) ||
02246 IsTunnelTile(v->tile) ||
02247 (v->type == VEH_TRAIN &&
02248 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02249 continue;
02250 }
02251
02252
02253
02254
02255 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02256
02257 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02258 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02259
02260 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02261 x = -x;
02262 y = -y;
02263 }
02264
02265 switch (effect_type) {
02266 case VE_TYPE_STEAM:
02267
02268
02269
02270
02271
02272 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02273 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02274 sound = true;
02275 }
02276 break;
02277
02278 case VE_TYPE_DIESEL: {
02279
02280
02281
02282
02283
02284
02285
02286
02287
02288
02289
02290 int power_weight_effect = 0;
02291 if (v->type == VEH_TRAIN) {
02292 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02293 }
02294 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02295 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02296 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02297 sound = true;
02298 }
02299 break;
02300 }
02301
02302 case VE_TYPE_ELECTRIC:
02303
02304
02305
02306
02307
02308
02309 if (GB(v->tick_counter, 0, 2) == 0 &&
02310 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02311 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02312 sound = true;
02313 }
02314 break;
02315
02316 default:
02317 break;
02318 }
02319 } while ((v = v->Next()) != NULL);
02320
02321 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02322 }
02323
02328 void Vehicle::SetNext(Vehicle *next)
02329 {
02330 assert(this != next);
02331
02332 if (this->next != NULL) {
02333
02334 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02335 v->first = this->next;
02336 }
02337 this->next->previous = NULL;
02338 }
02339
02340 this->next = next;
02341
02342 if (this->next != NULL) {
02343
02344 if (this->next->previous != NULL) this->next->previous->next = NULL;
02345 this->next->previous = this;
02346 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02347 v->first = this->first;
02348 }
02349 }
02350 }
02351
02357 void Vehicle::AddToShared(Vehicle *shared_chain)
02358 {
02359 assert(this->previous_shared == NULL && this->next_shared == NULL);
02360
02361 if (shared_chain->orders.list == NULL) {
02362 assert(shared_chain->previous_shared == NULL);
02363 assert(shared_chain->next_shared == NULL);
02364 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02365 }
02366
02367 this->next_shared = shared_chain->next_shared;
02368 this->previous_shared = shared_chain;
02369
02370 shared_chain->next_shared = this;
02371
02372 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02373
02374 shared_chain->orders.list->AddVehicle(this);
02375 }
02376
02380 void Vehicle::RemoveFromShared()
02381 {
02382
02383
02384 bool were_first = (this->FirstShared() == this);
02385 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02386
02387 this->orders.list->RemoveVehicle(this);
02388
02389 if (!were_first) {
02390
02391 this->previous_shared->next_shared = this->NextShared();
02392 }
02393
02394 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02395
02396
02397 if (this->orders.list->GetNumVehicles() == 1) {
02398
02399 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02400 InvalidateVehicleOrder(this->FirstShared(), 0);
02401 } else if (were_first) {
02402
02403
02404 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02405 }
02406
02407 this->next_shared = NULL;
02408 this->previous_shared = NULL;
02409 }
02410
02411 void VehiclesYearlyLoop()
02412 {
02413 Vehicle *v;
02414 FOR_ALL_VEHICLES(v) {
02415 if (v->IsPrimaryVehicle()) {
02416
02417 Money profit = v->GetDisplayProfitThisYear();
02418 if (v->age >= 730 && profit < 0) {
02419 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02420 SetDParam(0, v->index);
02421 SetDParam(1, profit);
02422 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
02423 }
02424 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02425 }
02426
02427 v->profit_last_year = v->profit_this_year;
02428 v->profit_this_year = 0;
02429 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02430 }
02431 }
02432 GroupStatistics::UpdateProfits();
02433 SetWindowClassesDirty(WC_TRAINS_LIST);
02434 SetWindowClassesDirty(WC_SHIPS_LIST);
02435 SetWindowClassesDirty(WC_ROADVEH_LIST);
02436 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02437 }
02438
02439
02449 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02450 {
02451 const Engine *e = Engine::GetIfValid(engine_type);
02452 assert(e != NULL);
02453
02454 switch (e->type) {
02455 case VEH_TRAIN:
02456 return (st->facilities & FACIL_TRAIN) != 0;
02457
02458 case VEH_ROAD:
02459
02460
02461
02462 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02463
02464 case VEH_SHIP:
02465 return (st->facilities & FACIL_DOCK) != 0;
02466
02467 case VEH_AIRCRAFT:
02468 return (st->facilities & FACIL_AIRPORT) != 0 &&
02469 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02470
02471 default:
02472 return false;
02473 }
02474 }
02475
02482 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02483 {
02484 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02485
02486 return CanVehicleUseStation(v->engine_type, st);
02487 }
02488
02494 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02495 {
02496 assert(this->IsGroundVehicle());
02497 if (this->type == VEH_TRAIN) {
02498 return &Train::From(this)->gcache;
02499 } else {
02500 return &RoadVehicle::From(this)->gcache;
02501 }
02502 }
02503
02509 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02510 {
02511 assert(this->IsGroundVehicle());
02512 if (this->type == VEH_TRAIN) {
02513 return &Train::From(this)->gcache;
02514 } else {
02515 return &RoadVehicle::From(this)->gcache;
02516 }
02517 }
02518
02524 uint16 &Vehicle::GetGroundVehicleFlags()
02525 {
02526 assert(this->IsGroundVehicle());
02527 if (this->type == VEH_TRAIN) {
02528 return Train::From(this)->gv_flags;
02529 } else {
02530 return RoadVehicle::From(this)->gv_flags;
02531 }
02532 }
02533
02539 const uint16 &Vehicle::GetGroundVehicleFlags() const
02540 {
02541 assert(this->IsGroundVehicle());
02542 if (this->type == VEH_TRAIN) {
02543 return Train::From(this)->gv_flags;
02544 } else {
02545 return RoadVehicle::From(this)->gv_flags;
02546 }
02547 }
02548
02557 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02558 {
02559 if (v->type == VEH_TRAIN) {
02560 Train *u = Train::From(v);
02561
02562 u = u->GetFirstEnginePart();
02563
02564
02565 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02566 do {
02567
02568 set.Include(u->index);
02569
02570
02571 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02572
02573 u = u->Next();
02574 } while (u != NULL && u->IsArticulatedPart());
02575 }
02576 }
02577 }