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 (this->ServiceIntervalIsPercent() ?
00115 (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
00116 (this->date_of_last_service + this->GetServiceInterval() >= _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)) {
00840
00841 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00842 v->MarkDirty();
00843 }
00844 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00845
00846 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
00847 }
00848 }
00849
00850
00851 v->OnNewDay();
00852 }
00853 }
00854
00855 void CallVehicleTicks()
00856 {
00857 _vehicles_to_autoreplace.Clear();
00858
00859 RunVehicleDayProc();
00860
00861 Station *st;
00862 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00863
00864 Vehicle *v;
00865 FOR_ALL_VEHICLES(v) {
00866
00867 if (!v->Tick()) {
00868 assert(Vehicle::Get(vehicle_index) == NULL);
00869 continue;
00870 }
00871
00872 assert(Vehicle::Get(vehicle_index) == v);
00873
00874 switch (v->type) {
00875 default: break;
00876
00877 case VEH_TRAIN:
00878 case VEH_ROAD:
00879 case VEH_AIRCRAFT:
00880 case VEH_SHIP:
00881 if (v->vcache.cached_cargo_age_period != 0) {
00882 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
00883 if (--v->cargo_age_counter == 0) {
00884 v->cargo.AgeCargo();
00885 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
00886 }
00887 }
00888
00889 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00890 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00891 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00892
00893 v->motion_counter += v->cur_speed;
00894
00895 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00896
00897
00898 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00899 }
00900 }
00901
00902 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00903 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00904 v = it->first;
00905
00906 cur_company.Change(v->owner);
00907
00908
00909
00910
00911 if (it->second) v->vehstatus &= ~VS_STOPPED;
00912
00913
00914 int x = v->x_pos;
00915 int y = v->y_pos;
00916 int z = v->z_pos;
00917
00918 const Company *c = Company::Get(_current_company);
00919 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00920 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00921 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00922
00923 if (!IsLocalCompany()) continue;
00924
00925 if (res.Succeeded()) {
00926 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00927 continue;
00928 }
00929
00930 StringID error_message = res.GetErrorMessage();
00931 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00932
00933 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00934
00935 StringID message;
00936 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00937 message = error_message;
00938 } else {
00939 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00940 }
00941
00942 SetDParam(0, v->index);
00943 SetDParam(1, error_message);
00944 AddVehicleAdviceNewsItem(message, v->index);
00945 }
00946
00947 cur_company.Restore();
00948 }
00949
00954 static void DoDrawVehicle(const Vehicle *v)
00955 {
00956 SpriteID image = v->cur_image;
00957 PaletteID pal = PAL_NONE;
00958
00959 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00960
00961
00962 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
00963
00964 if (v->type == VEH_EFFECT) {
00965
00966
00967 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00968 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00969 }
00970
00971 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00972 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
00973 }
00974
00979 void ViewportAddVehicles(DrawPixelInfo *dpi)
00980 {
00981
00982 const int l = dpi->left;
00983 const int r = dpi->left + dpi->width;
00984 const int t = dpi->top;
00985 const int b = dpi->top + dpi->height;
00986
00987
00988 int xl, xu, yl, yu;
00989
00990 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
00991 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
00992 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
00993 } else {
00994
00995 xl = 0;
00996 xu = 0x3F;
00997 }
00998
00999 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
01000 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
01001 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
01002 } else {
01003
01004 yl = 0;
01005 yu = 0x3F << 6;
01006 }
01007
01008 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
01009 for (int x = xl;; x = (x + 1) & 0x3F) {
01010 const Vehicle *v = _vehicle_viewport_hash[x + y];
01011
01012 while (v != NULL) {
01013 if (!(v->vehstatus & VS_HIDDEN) &&
01014 l <= v->coord.right &&
01015 t <= v->coord.bottom &&
01016 r >= v->coord.left &&
01017 b >= v->coord.top) {
01018 DoDrawVehicle(v);
01019 }
01020 v = v->hash_viewport_next;
01021 }
01022
01023 if (x == xu) break;
01024 }
01025
01026 if (y == yu) break;
01027 }
01028 }
01029
01037 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01038 {
01039 Vehicle *found = NULL, *v;
01040 uint dist, best_dist = UINT_MAX;
01041
01042 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01043
01044 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01045 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01046
01047 FOR_ALL_VEHICLES(v) {
01048 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
01049 x >= v->coord.left && x <= v->coord.right &&
01050 y >= v->coord.top && y <= v->coord.bottom) {
01051
01052 dist = max(
01053 abs(((v->coord.left + v->coord.right) >> 1) - x),
01054 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01055 );
01056
01057 if (dist < best_dist) {
01058 found = v;
01059 best_dist = dist;
01060 }
01061 }
01062 }
01063
01064 return found;
01065 }
01066
01071 void DecreaseVehicleValue(Vehicle *v)
01072 {
01073 v->value -= v->value >> 8;
01074 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01075 }
01076
01077 static const byte _breakdown_chance[64] = {
01078 3, 3, 3, 3, 3, 3, 3, 3,
01079 4, 4, 5, 5, 6, 6, 7, 7,
01080 8, 8, 9, 9, 10, 10, 11, 11,
01081 12, 13, 13, 13, 13, 14, 15, 16,
01082 17, 19, 21, 25, 28, 31, 34, 37,
01083 40, 44, 48, 52, 56, 60, 64, 68,
01084 72, 80, 90, 100, 110, 120, 130, 140,
01085 150, 170, 190, 210, 230, 250, 250, 250,
01086 };
01087
01088 void CheckVehicleBreakdown(Vehicle *v)
01089 {
01090 int rel, rel_old;
01091
01092
01093 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01094 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01095
01096 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01097 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01098 v->cur_speed < 5 || _game_mode == GM_MENU) {
01099 return;
01100 }
01101
01102 uint32 r = Random();
01103
01104
01105 int chance = v->breakdown_chance + 1;
01106 if (Chance16I(1, 25, r)) chance += 25;
01107 v->breakdown_chance = min(255, chance);
01108
01109
01110 rel = v->reliability;
01111 if (v->type == VEH_SHIP) rel += 0x6666;
01112
01113
01114 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01115
01116
01117 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01118 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01119 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01120 v->breakdown_chance = 0;
01121 }
01122 }
01123
01130 bool Vehicle::HandleBreakdown()
01131 {
01132
01133
01134
01135
01136
01137 switch (this->breakdown_ctr) {
01138 case 0:
01139 return false;
01140
01141 case 2:
01142 this->breakdown_ctr = 1;
01143
01144 if (this->breakdowns_since_last_service != 255) {
01145 this->breakdowns_since_last_service++;
01146 }
01147
01148 if (this->type == VEH_AIRCRAFT) {
01149
01150 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01151 } else {
01152 this->cur_speed = 0;
01153
01154 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01155 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01156 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01157 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01158 }
01159
01160 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
01161 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01162 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01163 }
01164 }
01165
01166 this->MarkDirty();
01167 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01168 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01169
01170
01171 case 1:
01172
01173 if (this->type == VEH_AIRCRAFT) return false;
01174
01175
01176 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01177 if (--this->breakdown_delay == 0) {
01178 this->breakdown_ctr = 0;
01179 this->MarkDirty();
01180 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01181 }
01182 }
01183 return true;
01184
01185 default:
01186 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01187 return false;
01188 }
01189 }
01190
01195 void AgeVehicle(Vehicle *v)
01196 {
01197 if (v->age < MAX_DAY) {
01198 v->age++;
01199 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
01200 }
01201
01202 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
01203
01204 int age = v->age - v->max_age;
01205 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01206 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01207 v->reliability_spd_dec <<= 1;
01208 }
01209
01210 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01211
01212
01213 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01214
01215
01216 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
01217
01218 StringID str;
01219 if (age == -DAYS_IN_LEAP_YEAR) {
01220 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01221 } else if (age == 0) {
01222 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01223 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01224 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01225 } else {
01226 return;
01227 }
01228
01229 SetDParam(0, v->index);
01230 AddVehicleAdviceNewsItem(str, v->index);
01231 }
01232
01239 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
01240 {
01241 int count = 0;
01242 int max = 0;
01243 int cars = 0;
01244 int unloading = 0;
01245 bool loading = false;
01246
01247 bool is_loading = front->current_order.IsType(OT_LOADING);
01248
01249
01250 const Station *st = Station::GetIfValid(front->last_station_visited);
01251 assert(colour == NULL || (st != NULL && is_loading));
01252
01253 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
01254 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
01255
01256
01257 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
01258 count += v->cargo.Count();
01259 max += v->cargo_cap;
01260 if (v->cargo_cap != 0 && colour != NULL) {
01261 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01262 loading |= !order_no_load &&
01263 (order_full_load || HasBit(st->goods[v->cargo_type].acceptance_pickup, GoodsEntry::GES_PICKUP)) &&
01264 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
01265 cars++;
01266 }
01267 }
01268
01269 if (colour != NULL) {
01270 if (unloading == 0 && loading) {
01271 *colour = STR_PERCENT_UP;
01272 } else if (unloading == 0 && !loading) {
01273 *colour = STR_PERCENT_NONE;
01274 } else if (cars == unloading || !loading) {
01275 *colour = STR_PERCENT_DOWN;
01276 } else {
01277 *colour = STR_PERCENT_UP_DOWN;
01278 }
01279 }
01280
01281
01282 if (max == 0) return 100;
01283
01284
01285 return (count * 100) / max;
01286 }
01287
01292 void VehicleEnterDepot(Vehicle *v)
01293 {
01294
01295 assert(v == v->First());
01296
01297 switch (v->type) {
01298 case VEH_TRAIN: {
01299 Train *t = Train::From(v);
01300 SetWindowClassesDirty(WC_TRAINS_LIST);
01301
01302 SetDepotReservation(t->tile, false);
01303 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01304
01305 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01306 t->wait_counter = 0;
01307 t->force_proceed = TFP_NONE;
01308 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01309 t->ConsistChanged(true);
01310 break;
01311 }
01312
01313 case VEH_ROAD:
01314 SetWindowClassesDirty(WC_ROADVEH_LIST);
01315 break;
01316
01317 case VEH_SHIP: {
01318 SetWindowClassesDirty(WC_SHIPS_LIST);
01319 Ship *ship = Ship::From(v);
01320 ship->state = TRACK_BIT_DEPOT;
01321 ship->UpdateCache();
01322 ship->UpdateViewport(true, true);
01323 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01324 break;
01325 }
01326
01327 case VEH_AIRCRAFT:
01328 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01329 HandleAircraftEnterHangar(Aircraft::From(v));
01330 break;
01331 default: NOT_REACHED();
01332 }
01333 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01334
01335 if (v->type != VEH_TRAIN) {
01336
01337
01338 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01339 }
01340 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01341
01342 v->vehstatus |= VS_HIDDEN;
01343 v->cur_speed = 0;
01344
01345 VehicleServiceInDepot(v);
01346
01347
01348 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01349 v->MarkDirty();
01350
01351 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01352 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01353
01354 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01355 Order t = v->current_order;
01356 v->current_order.MakeDummy();
01357
01358
01359
01360 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01361 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01362 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01363
01364 return;
01365 }
01366
01367 if (t.IsRefit()) {
01368 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01369 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01370 cur_company.Restore();
01371
01372 if (cost.Failed()) {
01373 _vehicles_to_autoreplace[v] = false;
01374 if (v->owner == _local_company) {
01375
01376 SetDParam(0, v->index);
01377 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
01378 }
01379 } else if (cost.GetCost() != 0) {
01380 v->profit_this_year -= cost.GetCost() << 8;
01381 if (v->owner == _local_company) {
01382 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01383 }
01384 }
01385 }
01386
01387 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01388
01389 v->DeleteUnreachedImplicitOrders();
01390 UpdateVehicleTimetable(v, true);
01391 v->IncrementImplicitOrderIndex();
01392 }
01393 if (t.GetDepotActionType() & ODATFB_HALT) {
01394
01395 _vehicles_to_autoreplace[v] = false;
01396 if (v->owner == _local_company) {
01397 SetDParam(0, v->index);
01398 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
01399 }
01400 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01401 }
01402 }
01403 }
01404
01405
01411 void VehicleUpdatePosition(Vehicle *v)
01412 {
01413 UpdateVehicleTileHash(v, false);
01414 }
01415
01422 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01423 {
01424 int img = v->cur_image;
01425 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01426 const Sprite *spr = GetSprite(img, ST_NORMAL);
01427
01428 pt.x += spr->x_offs;
01429 pt.y += spr->y_offs;
01430
01431 UpdateVehicleViewportHash(v, pt.x, pt.y);
01432
01433 Rect old_coord = v->coord;
01434 v->coord.left = pt.x;
01435 v->coord.top = pt.y;
01436 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01437 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01438
01439 if (dirty) {
01440 if (old_coord.left == INVALID_COORD) {
01441 MarkSingleVehicleDirty(v);
01442 } else {
01443 MarkAllViewportsDirty(
01444 min(old_coord.left, v->coord.left),
01445 min(old_coord.top, v->coord.top),
01446 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01447 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01448 );
01449 }
01450 }
01451 }
01452
01457 void VehicleUpdatePositionAndViewport(Vehicle *v)
01458 {
01459 VehicleUpdatePosition(v);
01460 VehicleUpdateViewport(v, true);
01461 }
01462
01467 void MarkSingleVehicleDirty(const Vehicle *v)
01468 {
01469 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01470 }
01471
01477 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01478 {
01479 static const int8 _delta_coord[16] = {
01480 -1,-1,-1, 0, 1, 1, 1, 0,
01481 -1, 0, 1, 1, 1, 0,-1,-1,
01482 };
01483
01484 int x = v->x_pos + _delta_coord[v->direction];
01485 int y = v->y_pos + _delta_coord[v->direction + 8];
01486
01487 GetNewVehiclePosResult gp;
01488 gp.x = x;
01489 gp.y = y;
01490 gp.old_tile = v->tile;
01491 gp.new_tile = TileVirtXY(x, y);
01492 return gp;
01493 }
01494
01495 static const Direction _new_direction_table[] = {
01496 DIR_N, DIR_NW, DIR_W,
01497 DIR_NE, DIR_SE, DIR_SW,
01498 DIR_E, DIR_SE, DIR_S
01499 };
01500
01501 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01502 {
01503 int i = 0;
01504
01505 if (y >= v->y_pos) {
01506 if (y != v->y_pos) i += 3;
01507 i += 3;
01508 }
01509
01510 if (x >= v->x_pos) {
01511 if (x != v->x_pos) i++;
01512 i++;
01513 }
01514
01515 Direction dir = v->direction;
01516
01517 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01518 if (dirdiff == DIRDIFF_SAME) return dir;
01519 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01520 }
01521
01531 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01532 {
01533 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01534 }
01535
01543 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01544 {
01545
01546 const Vehicle *v;
01547 FOR_ALL_VEHICLES(v) {
01548 if (v->type == type && v->owner == owner) {
01549 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01550 }
01551 }
01552
01553 if (this->maxid == 0) return;
01554
01555
01556
01557
01558 this->cache = CallocT<bool>(this->maxid + 2);
01559
01560
01561 FOR_ALL_VEHICLES(v) {
01562 if (v->type == type && v->owner == owner) {
01563 this->cache[v->unitnumber] = true;
01564 }
01565 }
01566 }
01567
01569 UnitID FreeUnitIDGenerator::NextID()
01570 {
01571 if (this->maxid <= this->curid) return ++this->curid;
01572
01573 while (this->cache[++this->curid]) { }
01574
01575 return this->curid;
01576 }
01577
01583 UnitID GetFreeUnitNumber(VehicleType type)
01584 {
01585
01586 uint max_veh;
01587 switch (type) {
01588 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01589 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01590 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01591 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01592 default: NOT_REACHED();
01593 }
01594
01595 const Company *c = Company::Get(_current_company);
01596 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01597
01598 FreeUnitIDGenerator gen(type, _current_company);
01599
01600 return gen.NextID();
01601 }
01602
01603
01612 bool CanBuildVehicleInfrastructure(VehicleType type)
01613 {
01614 assert(IsCompanyBuildableVehicleType(type));
01615
01616 if (!Company::IsValidID(_local_company)) return false;
01617 if (!_settings_client.gui.disable_unsuitable_building) return true;
01618
01619 UnitID max;
01620 switch (type) {
01621 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01622 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01623 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01624 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01625 default: NOT_REACHED();
01626 }
01627
01628
01629 if (max > 0) {
01630
01631 const Engine *e;
01632 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01633 if (HasBit(e->company_avail, _local_company)) return true;
01634 }
01635 return false;
01636 }
01637
01638
01639 const Vehicle *v;
01640 FOR_ALL_VEHICLES(v) {
01641 if (v->owner == _local_company && v->type == type) return true;
01642 }
01643
01644 return false;
01645 }
01646
01647
01655 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01656 {
01657 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01658 const Engine *e = Engine::Get(engine_type);
01659 switch (e->type) {
01660 default: NOT_REACHED();
01661 case VEH_TRAIN:
01662 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01663
01664
01665 engine_type = parent_engine_type;
01666 e = Engine::Get(engine_type);
01667
01668 }
01669
01670 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01671 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01672 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01673 if (!CargoSpec::Get(cargo_type)->is_freight) {
01674 if (parent_engine_type == INVALID_ENGINE) {
01675 return LS_PASSENGER_WAGON_STEAM;
01676 } else {
01677 switch (RailVehInfo(parent_engine_type)->engclass) {
01678 default: NOT_REACHED();
01679 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01680 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01681 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01682 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01683 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01684 }
01685 }
01686 } else {
01687 return LS_FREIGHT_WAGON;
01688 }
01689 } else {
01690 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01691
01692 switch (e->u.rail.engclass) {
01693 default: NOT_REACHED();
01694 case EC_STEAM: return LS_STEAM;
01695 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01696 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01697 case EC_MONORAIL: return LS_MONORAIL;
01698 case EC_MAGLEV: return LS_MAGLEV;
01699 }
01700 }
01701
01702 case VEH_ROAD:
01703
01704 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01705 engine_type = parent_engine_type;
01706 e = Engine::Get(engine_type);
01707 cargo_type = v->First()->cargo_type;
01708 }
01709 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01710 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01711
01712
01713 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01714
01715 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01716 } else {
01717
01718 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01719 }
01720
01721 case VEH_SHIP:
01722 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01723 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01724 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01725
01726 case VEH_AIRCRAFT:
01727 switch (e->u.air.subtype) {
01728 case AIR_HELI: return LS_HELICOPTER;
01729 case AIR_CTOL: return LS_SMALL_PLANE;
01730 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01731 default: NOT_REACHED();
01732 }
01733 }
01734 }
01735
01745 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01746 {
01747 const Company *c = Company::Get(company);
01748 LiveryScheme scheme = LS_DEFAULT;
01749
01750
01751
01752 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01753
01754 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01755
01756
01757 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01758 }
01759
01760 return &c->livery[scheme];
01761 }
01762
01763
01764 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01765 {
01766 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01767
01768
01769 if (map != PAL_NONE) return map;
01770
01771 const Engine *e = Engine::Get(engine_type);
01772
01773
01774 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01775 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01776
01777 if (callback != CALLBACK_FAILED) {
01778 assert_compile(PAL_NONE == 0);
01779 map = GB(callback, 0, 14);
01780
01781
01782 if (!HasBit(callback, 14)) {
01783
01784 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01785 return map;
01786 }
01787 }
01788 }
01789
01790 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01791
01792 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01793
01794
01795 if (!Company::IsValidID(company)) return map;
01796
01797 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01798
01799 map += livery->colour1;
01800 if (twocc) map += livery->colour2 * 16;
01801
01802
01803 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01804 return map;
01805 }
01806
01813 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01814 {
01815 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01816 }
01817
01823 PaletteID GetVehiclePalette(const Vehicle *v)
01824 {
01825 if (v->IsGroundVehicle()) {
01826 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01827 }
01828
01829 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01830 }
01831
01835 void Vehicle::DeleteUnreachedImplicitOrders()
01836 {
01837 if (this->IsGroundVehicle()) {
01838 uint16 &gv_flags = this->GetGroundVehicleFlags();
01839 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01840
01841 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01842 this->cur_implicit_order_index = this->cur_real_order_index;
01843 InvalidateVehicleOrder(this, 0);
01844 return;
01845 }
01846 }
01847
01848 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01849 while (order != NULL) {
01850 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01851
01852 if (order->IsType(OT_IMPLICIT)) {
01853 DeleteOrder(this, this->cur_implicit_order_index);
01854
01855 order = this->GetOrder(this->cur_implicit_order_index);
01856 } else {
01857
01858 order = order->next;
01859 this->cur_implicit_order_index++;
01860 }
01861
01862
01863 if (order == NULL) {
01864 order = this->GetOrder(0);
01865 this->cur_implicit_order_index = 0;
01866 }
01867 }
01868 }
01869
01874 void Vehicle::BeginLoading()
01875 {
01876 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01877
01878 if (this->current_order.IsType(OT_GOTO_STATION) &&
01879 this->current_order.GetDestination() == this->last_station_visited) {
01880 this->DeleteUnreachedImplicitOrders();
01881
01882
01883 this->current_order.MakeLoading(true);
01884 UpdateVehicleTimetable(this, true);
01885
01886
01887
01888
01889
01890
01891 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01892
01893 } else {
01894
01895
01896
01897
01898
01899 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01900 if (this->IsGroundVehicle() && in_list != NULL &&
01901 (!in_list->IsType(OT_IMPLICIT) ||
01902 in_list->GetDestination() != this->last_station_visited)) {
01903 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01904
01905 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01906 if (prev_order == NULL ||
01907 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01908 prev_order->GetDestination() != this->last_station_visited) {
01909
01910
01911
01912 int target_index = this->cur_implicit_order_index;
01913 bool found = false;
01914 while (target_index != this->cur_real_order_index) {
01915 const Order *order = this->GetOrder(target_index);
01916 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01917 found = true;
01918 break;
01919 }
01920 target_index++;
01921 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01922 assert(target_index != this->cur_implicit_order_index);
01923 }
01924
01925 if (found) {
01926 if (suppress_implicit_orders) {
01927
01928 this->cur_implicit_order_index = target_index;
01929 InvalidateVehicleOrder(this, 0);
01930 } else {
01931
01932 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01933 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01934 if (order->IsType(OT_IMPLICIT)) {
01935 DeleteOrder(this, this->cur_implicit_order_index);
01936
01937 order = this->GetOrder(this->cur_implicit_order_index);
01938 } else {
01939
01940 order = order->next;
01941 this->cur_implicit_order_index++;
01942 }
01943
01944
01945 if (order == NULL) {
01946 order = this->GetOrder(0);
01947 this->cur_implicit_order_index = 0;
01948 }
01949 assert(order != NULL);
01950 }
01951 }
01952 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01953
01954 Order *implicit_order = new Order();
01955 implicit_order->MakeImplicit(this->last_station_visited);
01956 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01957 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01958
01959
01960
01961 uint16 &gv_flags = this->GetGroundVehicleFlags();
01962 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01963 }
01964 }
01965 }
01966 this->current_order.MakeLoading(false);
01967 }
01968
01969 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01970
01971 PrepareUnload(this);
01972
01973 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01974 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
01975 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01976 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01977
01978 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01979 this->cur_speed = 0;
01980 this->MarkDirty();
01981 }
01982
01987 void Vehicle::LeaveStation()
01988 {
01989 assert(this->current_order.IsType(OT_LOADING));
01990
01991 delete this->cargo_payment;
01992
01993
01994 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01995
01996 this->current_order.MakeLeaveStation();
01997 Station *st = Station::Get(this->last_station_visited);
01998 st->loading_vehicles.remove(this);
01999
02000 HideFillingPercent(&this->fill_percent_te_id);
02001
02002 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02003
02004 if (IsTileType(this->tile, MP_STATION)) {
02005 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
02006 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02007 }
02008
02009 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02010 }
02011 }
02012
02013
02019 void Vehicle::HandleLoading(bool mode)
02020 {
02021 switch (this->current_order.GetType()) {
02022 case OT_LOADING: {
02023 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02024
02025
02026 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02027
02028 this->PlayLeaveStationSound();
02029
02030 this->LeaveStation();
02031
02032
02033 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02034 if (order == NULL ||
02035 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02036 order->GetDestination() != this->last_station_visited) {
02037 return;
02038 }
02039 break;
02040 }
02041
02042 case OT_DUMMY: break;
02043
02044 default: return;
02045 }
02046
02047 this->IncrementImplicitOrderIndex();
02048 }
02049
02056 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02057 {
02058 CommandCost ret = CheckOwnership(this->owner);
02059 if (ret.Failed()) return ret;
02060
02061 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02062 if (this->IsStoppedInDepot()) return CMD_ERROR;
02063
02064 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02065 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02066 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02067
02068
02069
02070 if (flags & DC_EXEC) {
02071 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02072 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02073 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02074 }
02075 return CommandCost();
02076 }
02077
02078 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02079 if (flags & DC_EXEC) {
02080
02081
02082 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02083
02084 if (this->IsGroundVehicle()) {
02085 uint16 &gv_flags = this->GetGroundVehicleFlags();
02086 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02087 }
02088
02089 this->current_order.MakeDummy();
02090 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02091 }
02092 return CommandCost();
02093 }
02094
02095 TileIndex location;
02096 DestinationID destination;
02097 bool reverse;
02098 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};
02099 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02100
02101 if (flags & DC_EXEC) {
02102 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02103
02104 if (this->IsGroundVehicle()) {
02105 uint16 &gv_flags = this->GetGroundVehicleFlags();
02106 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02107 }
02108
02109 this->dest_tile = location;
02110 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02111 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02112 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02113
02114
02115 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02116
02117 if (this->type == VEH_AIRCRAFT) {
02118 Aircraft *a = Aircraft::From(this);
02119 if (a->state == FLYING && a->targetairport != destination) {
02120
02121 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02122 AircraftNextAirportPos_and_Order(a);
02123 }
02124 }
02125 }
02126
02127 return CommandCost();
02128
02129 }
02130
02135 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02136 {
02137 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02138 const Engine *e = this->GetEngine();
02139
02140
02141 byte visual_effect;
02142 switch (e->type) {
02143 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02144 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02145 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02146 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02147 }
02148
02149
02150 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02151 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02152
02153 if (callback != CALLBACK_FAILED) {
02154 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02155
02156 callback = GB(callback, 0, 8);
02157
02158
02159 if (callback == VE_DEFAULT) {
02160 assert(HasBit(callback, VE_DISABLE_EFFECT));
02161 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02162 }
02163 visual_effect = callback;
02164 }
02165 }
02166
02167
02168 if (visual_effect == VE_DEFAULT ||
02169 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02170
02171
02172 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02173 if (visual_effect == VE_DEFAULT) {
02174 visual_effect = 1 << VE_DISABLE_EFFECT;
02175 } else {
02176 SetBit(visual_effect, VE_DISABLE_EFFECT);
02177 }
02178 } else {
02179 if (visual_effect == VE_DEFAULT) {
02180
02181 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02182 }
02183 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02184 }
02185 }
02186
02187 this->vcache.cached_vis_effect = visual_effect;
02188
02189 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02190 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02191 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02192 }
02193 }
02194
02195 static const int8 _vehicle_smoke_pos[8] = {
02196 1, 1, 1, 0, -1, -1, -1, 0
02197 };
02198
02203 void Vehicle::ShowVisualEffect() const
02204 {
02205 assert(this->IsPrimaryVehicle());
02206 bool sound = false;
02207
02208
02209
02210
02211
02212
02213 if (_settings_game.vehicle.smoke_amount == 0 ||
02214 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02215 this->cur_speed < 2) {
02216 return;
02217 }
02218
02219 uint max_speed = this->vcache.cached_max_speed;
02220 if (this->type == VEH_TRAIN) {
02221 const Train *t = Train::From(this);
02222
02223
02224
02225
02226 if (HasBit(t->flags, VRF_REVERSING) ||
02227 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02228 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02229 return;
02230 }
02231
02232 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02233 max_speed = min(max_speed, this->current_order.max_speed);
02234 }
02235 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02236
02237 const Vehicle *v = this;
02238
02239 do {
02240 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02241 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02242 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02243
02244
02245
02246
02247
02248
02249
02250
02251 if (disable_effect ||
02252 v->vehstatus & VS_HIDDEN ||
02253 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02254 IsDepotTile(v->tile) ||
02255 IsTunnelTile(v->tile) ||
02256 (v->type == VEH_TRAIN &&
02257 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02258 continue;
02259 }
02260
02261
02262
02263
02264 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02265
02266 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02267 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02268
02269 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02270 x = -x;
02271 y = -y;
02272 }
02273
02274 switch (effect_type) {
02275 case VE_TYPE_STEAM:
02276
02277
02278
02279
02280
02281 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02282 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02283 sound = true;
02284 }
02285 break;
02286
02287 case VE_TYPE_DIESEL: {
02288
02289
02290
02291
02292
02293
02294
02295
02296
02297
02298
02299 int power_weight_effect = 0;
02300 if (v->type == VEH_TRAIN) {
02301 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02302 }
02303 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02304 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02305 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02306 sound = true;
02307 }
02308 break;
02309 }
02310
02311 case VE_TYPE_ELECTRIC:
02312
02313
02314
02315
02316
02317
02318 if (GB(v->tick_counter, 0, 2) == 0 &&
02319 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02320 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02321 sound = true;
02322 }
02323 break;
02324
02325 default:
02326 break;
02327 }
02328 } while ((v = v->Next()) != NULL);
02329
02330 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02331 }
02332
02337 void Vehicle::SetNext(Vehicle *next)
02338 {
02339 assert(this != next);
02340
02341 if (this->next != NULL) {
02342
02343 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02344 v->first = this->next;
02345 }
02346 this->next->previous = NULL;
02347 }
02348
02349 this->next = next;
02350
02351 if (this->next != NULL) {
02352
02353 if (this->next->previous != NULL) this->next->previous->next = NULL;
02354 this->next->previous = this;
02355 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02356 v->first = this->first;
02357 }
02358 }
02359 }
02360
02366 void Vehicle::AddToShared(Vehicle *shared_chain)
02367 {
02368 assert(this->previous_shared == NULL && this->next_shared == NULL);
02369
02370 if (shared_chain->orders.list == NULL) {
02371 assert(shared_chain->previous_shared == NULL);
02372 assert(shared_chain->next_shared == NULL);
02373 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02374 }
02375
02376 this->next_shared = shared_chain->next_shared;
02377 this->previous_shared = shared_chain;
02378
02379 shared_chain->next_shared = this;
02380
02381 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02382
02383 shared_chain->orders.list->AddVehicle(this);
02384 }
02385
02389 void Vehicle::RemoveFromShared()
02390 {
02391
02392
02393 bool were_first = (this->FirstShared() == this);
02394 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02395
02396 this->orders.list->RemoveVehicle(this);
02397
02398 if (!were_first) {
02399
02400 this->previous_shared->next_shared = this->NextShared();
02401 }
02402
02403 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02404
02405
02406 if (this->orders.list->GetNumVehicles() == 1) {
02407
02408 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02409 InvalidateVehicleOrder(this->FirstShared(), 0);
02410 } else if (were_first) {
02411
02412
02413 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02414 }
02415
02416 this->next_shared = NULL;
02417 this->previous_shared = NULL;
02418 }
02419
02420 void VehiclesYearlyLoop()
02421 {
02422 Vehicle *v;
02423 FOR_ALL_VEHICLES(v) {
02424 if (v->IsPrimaryVehicle()) {
02425
02426 Money profit = v->GetDisplayProfitThisYear();
02427 if (v->age >= 730 && profit < 0) {
02428 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02429 SetDParam(0, v->index);
02430 SetDParam(1, profit);
02431 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
02432 }
02433 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02434 }
02435
02436 v->profit_last_year = v->profit_this_year;
02437 v->profit_this_year = 0;
02438 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02439 }
02440 }
02441 GroupStatistics::UpdateProfits();
02442 SetWindowClassesDirty(WC_TRAINS_LIST);
02443 SetWindowClassesDirty(WC_SHIPS_LIST);
02444 SetWindowClassesDirty(WC_ROADVEH_LIST);
02445 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02446 }
02447
02448
02458 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02459 {
02460 const Engine *e = Engine::GetIfValid(engine_type);
02461 assert(e != NULL);
02462
02463 switch (e->type) {
02464 case VEH_TRAIN:
02465 return (st->facilities & FACIL_TRAIN) != 0;
02466
02467 case VEH_ROAD:
02468
02469
02470
02471 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02472
02473 case VEH_SHIP:
02474 return (st->facilities & FACIL_DOCK) != 0;
02475
02476 case VEH_AIRCRAFT:
02477 return (st->facilities & FACIL_AIRPORT) != 0 &&
02478 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02479
02480 default:
02481 return false;
02482 }
02483 }
02484
02491 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02492 {
02493 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02494
02495 return CanVehicleUseStation(v->engine_type, st);
02496 }
02497
02503 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02504 {
02505 assert(this->IsGroundVehicle());
02506 if (this->type == VEH_TRAIN) {
02507 return &Train::From(this)->gcache;
02508 } else {
02509 return &RoadVehicle::From(this)->gcache;
02510 }
02511 }
02512
02518 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02519 {
02520 assert(this->IsGroundVehicle());
02521 if (this->type == VEH_TRAIN) {
02522 return &Train::From(this)->gcache;
02523 } else {
02524 return &RoadVehicle::From(this)->gcache;
02525 }
02526 }
02527
02533 uint16 &Vehicle::GetGroundVehicleFlags()
02534 {
02535 assert(this->IsGroundVehicle());
02536 if (this->type == VEH_TRAIN) {
02537 return Train::From(this)->gv_flags;
02538 } else {
02539 return RoadVehicle::From(this)->gv_flags;
02540 }
02541 }
02542
02548 const uint16 &Vehicle::GetGroundVehicleFlags() const
02549 {
02550 assert(this->IsGroundVehicle());
02551 if (this->type == VEH_TRAIN) {
02552 return Train::From(this)->gv_flags;
02553 } else {
02554 return RoadVehicle::From(this)->gv_flags;
02555 }
02556 }
02557
02566 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02567 {
02568 if (v->type == VEH_TRAIN) {
02569 Train *u = Train::From(v);
02570
02571 u = u->GetFirstEnginePart();
02572
02573
02574 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02575 do {
02576
02577 set.Include(u->index);
02578
02579
02580 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02581
02582 u = u->Next();
02583 } while (u != NULL && u->IsArticulatedPart());
02584 }
02585 }
02586 }