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