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