00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "error.h"
00014 #include "roadveh.h"
00015 #include "ship.h"
00016 #include "spritecache.h"
00017 #include "timetable.h"
00018 #include "viewport_func.h"
00019 #include "news_func.h"
00020 #include "command_func.h"
00021 #include "company_func.h"
00022 #include "train.h"
00023 #include "aircraft.h"
00024 #include "newgrf_debug.h"
00025 #include "newgrf_sound.h"
00026 #include "newgrf_station.h"
00027 #include "group_gui.h"
00028 #include "strings_func.h"
00029 #include "zoom_func.h"
00030 #include "date_func.h"
00031 #include "vehicle_func.h"
00032 #include "autoreplace_func.h"
00033 #include "autoreplace_gui.h"
00034 #include "station_base.h"
00035 #include "ai/ai.hpp"
00036 #include "depot_func.h"
00037 #include "network/network.h"
00038 #include "core/pool_func.hpp"
00039 #include "economy_base.h"
00040 #include "articulated_vehicles.h"
00041 #include "roadstop_base.h"
00042 #include "core/random_func.hpp"
00043 #include "core/backup_type.hpp"
00044 #include "order_backup.h"
00045 #include "sound_func.h"
00046 #include "effectvehicle_func.h"
00047 #include "effectvehicle_base.h"
00048 #include "vehiclelist.h"
00049 #include "bridge_map.h"
00050 #include "tunnel_map.h"
00051 #include "depot_map.h"
00052 #include "gamelog.h"
00053
00054 #include "table/strings.h"
00055
00056 #define GEN_HASH(x, y) ((GB((y), 6 + ZOOM_LVL_SHIFT, 6) << 6) + GB((x), 7 + ZOOM_LVL_SHIFT, 6))
00057
00058 VehicleID _new_vehicle_id;
00059 uint16 _returned_refit_capacity;
00060 uint16 _returned_mail_refit_capacity;
00061
00062
00064 VehiclePool _vehicle_pool("Vehicle");
00065 INSTANTIATE_POOL_METHODS(Vehicle)
00066
00067
00073 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
00074 {
00075
00076
00077
00078
00079 assert(c == Company::Get(this->owner));
00080
00081 if (use_renew_setting && !c->settings.engine_renew) return false;
00082 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00083
00084
00085 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
00086
00087 return true;
00088 }
00089
00090 void VehicleServiceInDepot(Vehicle *v)
00091 {
00092 v->date_of_last_service = _date;
00093 v->breakdowns_since_last_service = 0;
00094 v->reliability = v->GetEngine()->reliability;
00095
00096 v->breakdown_chance /= 4;
00097 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00098 }
00099
00106 bool Vehicle::NeedsServicing() const
00107 {
00108
00109
00110 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00111
00112
00113 const Company *c = Company::Get(this->owner);
00114 if (c->settings.vehicle.servint_ispercent ?
00115 (this->reliability >= this->GetEngine()->reliability * (100 - this->service_interval) / 100) :
00116 (this->date_of_last_service + this->service_interval >= _date)) {
00117 return false;
00118 }
00119
00120
00121
00122 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00123 _settings_game.difficulty.vehicle_breakdowns != 0) {
00124 return true;
00125 }
00126
00127
00128
00129
00130 bool pending_replace = false;
00131 Money needed_money = c->settings.engine_renew_money;
00132 if (needed_money > c->money) return false;
00133
00134 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00135 bool replace_when_old = false;
00136 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
00137
00138
00139 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00140
00141 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
00142
00143
00144 uint32 available_cargo_types, union_mask;
00145 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00146
00147 if (union_mask != 0) {
00148 CargoID cargo_type;
00149
00150 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
00151
00152
00153 if (cargo_type != CT_INVALID) {
00154
00155 if (!HasBit(available_cargo_types, cargo_type)) continue;
00156 }
00157 }
00158
00159
00160
00161 pending_replace = true;
00162 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00163 if (needed_money > c->money) return false;
00164 }
00165
00166 return pending_replace;
00167 }
00168
00174 bool Vehicle::NeedsAutomaticServicing() const
00175 {
00176 if (this->HasDepotOrder()) return false;
00177 if (this->current_order.IsType(OT_LOADING)) return false;
00178 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00179 return NeedsServicing();
00180 }
00181
00182 uint Vehicle::Crash(bool flooded)
00183 {
00184 assert((this->vehstatus & VS_CRASHED) == 0);
00185 assert(this->Previous() == NULL);
00186
00187 uint pass = 0;
00188
00189 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00190
00191 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00192 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00193 v->vehstatus |= VS_CRASHED;
00194 MarkSingleVehicleDirty(v);
00195 }
00196
00197
00198 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00199 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
00200 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00201 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00202
00203 delete this->cargo_payment;
00204 this->cargo_payment = NULL;
00205
00206 return RandomRange(pass + 1);
00207 }
00208
00209
00218 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00219 {
00220 const Engine *e = Engine::Get(engine);
00221 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
00222
00223 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00224 SetBit(grfconfig->grf_bugs, bug_type);
00225 SetDParamStr(0, grfconfig->GetName());
00226 SetDParam(1, engine);
00227 ShowErrorMessage(part1, part2, WL_CRITICAL);
00228 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00229 }
00230
00231
00232 char buffer[512];
00233
00234 SetDParamStr(0, grfconfig->GetName());
00235 GetString(buffer, part1, lastof(buffer));
00236 DEBUG(grf, 0, "%s", buffer + 3);
00237
00238 SetDParam(1, engine);
00239 GetString(buffer, part2, lastof(buffer));
00240 DEBUG(grf, 0, "%s", buffer + 3);
00241 }
00242
00248 void VehicleLengthChanged(const Vehicle *u)
00249 {
00250
00251 const Engine *engine = u->GetEngine();
00252 uint32 grfid = engine->grf_prop.grffile->grfid;
00253 GRFConfig *grfconfig = GetGRFConfig(grfid);
00254 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
00255 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
00256 }
00257 }
00258
00263 Vehicle::Vehicle(VehicleType type)
00264 {
00265 this->type = type;
00266 this->coord.left = INVALID_COORD;
00267 this->group_id = DEFAULT_GROUP;
00268 this->fill_percent_te_id = INVALID_TE_ID;
00269 this->first = this;
00270 this->colourmap = PAL_NONE;
00271 this->cargo_age_counter = 1;
00272 }
00273
00278 byte VehicleRandomBits()
00279 {
00280 return GB(Random(), 0, 8);
00281 }
00282
00283
00284
00285 const int HASH_BITS = 7;
00286 const int HASH_SIZE = 1 << HASH_BITS;
00287 const int HASH_MASK = HASH_SIZE - 1;
00288 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00289 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00290
00291
00292
00293 const int HASH_RES = 0;
00294
00295 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
00296
00297 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00298 {
00299 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00300 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00301 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00302 for (; v != NULL; v = v->hash_tile_next) {
00303 Vehicle *a = proc(v, data);
00304 if (find_first && a != NULL) return a;
00305 }
00306 if (x == xu) break;
00307 }
00308 if (y == yu) break;
00309 }
00310
00311 return NULL;
00312 }
00313
00314
00326 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00327 {
00328 const int COLL_DIST = 6;
00329
00330
00331 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00332 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00333 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00334 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00335
00336 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
00337 }
00338
00353 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00354 {
00355 VehicleFromPosXY(x, y, data, proc, false);
00356 }
00357
00369 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00370 {
00371 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00372 }
00373
00384 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00385 {
00386 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00387 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00388
00389 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00390 for (; v != NULL; v = v->hash_tile_next) {
00391 if (v->tile != tile) continue;
00392
00393 Vehicle *a = proc(v, data);
00394 if (find_first && a != NULL) return a;
00395 }
00396
00397 return NULL;
00398 }
00399
00413 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00414 {
00415 VehicleFromPos(tile, data, proc, false);
00416 }
00417
00428 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00429 {
00430 return VehicleFromPos(tile, data, proc, true) != NULL;
00431 }
00432
00439 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00440 {
00441 int z = *(int*)data;
00442
00443 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00444 if (v->z_pos > z) return NULL;
00445
00446 return v;
00447 }
00448
00454 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00455 {
00456 int z = GetTileMaxPixelZ(tile);
00457
00458
00459
00460
00461
00462 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00463 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00464 return CommandCost();
00465 }
00466
00468 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00469 {
00470 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00471 if (v == (const Vehicle *)data) return NULL;
00472
00473 return v;
00474 }
00475
00483 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00484 {
00485
00486
00487
00488
00489 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00490 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00491
00492 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00493 return CommandCost();
00494 }
00495
00496 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00497 {
00498 TrackBits rail_bits = *(TrackBits *)data;
00499
00500 if (v->type != VEH_TRAIN) return NULL;
00501
00502 Train *t = Train::From(v);
00503 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00504
00505 return v;
00506 }
00507
00516 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00517 {
00518
00519
00520
00521
00522 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00523 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00524 return CommandCost();
00525 }
00526
00527 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
00528 {
00529 Vehicle **old_hash = v->hash_tile_current;
00530 Vehicle **new_hash;
00531
00532 if (remove) {
00533 new_hash = NULL;
00534 } else {
00535 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00536 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00537 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00538 }
00539
00540 if (old_hash == new_hash) return;
00541
00542
00543 if (old_hash != NULL) {
00544 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
00545 *v->hash_tile_prev = v->hash_tile_next;
00546 }
00547
00548
00549 if (new_hash != NULL) {
00550 v->hash_tile_next = *new_hash;
00551 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
00552 v->hash_tile_prev = new_hash;
00553 *new_hash = v;
00554 }
00555
00556
00557 v->hash_tile_current = new_hash;
00558 }
00559
00560 static Vehicle *_vehicle_viewport_hash[0x1000];
00561
00562 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
00563 {
00564 Vehicle **old_hash, **new_hash;
00565 int old_x = v->coord.left;
00566 int old_y = v->coord.top;
00567
00568 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
00569 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
00570
00571 if (old_hash == new_hash) return;
00572
00573
00574 if (old_hash != NULL) {
00575 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
00576 *v->hash_viewport_prev = v->hash_viewport_next;
00577 }
00578
00579
00580 if (new_hash != NULL) {
00581 v->hash_viewport_next = *new_hash;
00582 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
00583 v->hash_viewport_prev = new_hash;
00584 *new_hash = v;
00585 }
00586 }
00587
00588 void ResetVehicleHash()
00589 {
00590 Vehicle *v;
00591 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
00592 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
00593 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
00594 }
00595
00596 void ResetVehicleColourMap()
00597 {
00598 Vehicle *v;
00599 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00600 }
00601
00606 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00607 static AutoreplaceMap _vehicles_to_autoreplace;
00608
00609 void InitializeVehicles()
00610 {
00611 _vehicles_to_autoreplace.Reset();
00612 ResetVehicleHash();
00613 }
00614
00615 uint CountVehiclesInChain(const Vehicle *v)
00616 {
00617 uint count = 0;
00618 do count++; while ((v = v->Next()) != NULL);
00619 return count;
00620 }
00621
00626 bool Vehicle::IsEngineCountable() const
00627 {
00628 switch (this->type) {
00629 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00630 case VEH_TRAIN:
00631 return !this->IsArticulatedPart() &&
00632 !Train::From(this)->IsRearDualheaded();
00633 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00634 case VEH_SHIP: return true;
00635 default: return false;
00636 }
00637 }
00638
00643 bool Vehicle::HasEngineType() const
00644 {
00645 switch (this->type) {
00646 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00647 case VEH_TRAIN:
00648 case VEH_ROAD:
00649 case VEH_SHIP: return true;
00650 default: return false;
00651 }
00652 }
00653
00659 const Engine *Vehicle::GetEngine() const
00660 {
00661 return Engine::Get(this->engine_type);
00662 }
00663
00669 const GRFFile *Vehicle::GetGRF() const
00670 {
00671 return this->GetEngine()->GetGRF();
00672 }
00673
00679 uint32 Vehicle::GetGRFID() const
00680 {
00681 return this->GetEngine()->GetGRFID();
00682 }
00683
00691 void Vehicle::HandlePathfindingResult(bool path_found)
00692 {
00693 if (path_found) {
00694
00695 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00696
00697
00698 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00699
00700 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00701 return;
00702 }
00703
00704
00705 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00706
00707
00708 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00709
00710 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
00711 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00712 SetDParam(0, this->index);
00713 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
00714 }
00715 }
00716
00718 void Vehicle::PreDestructor()
00719 {
00720 if (CleaningPool()) return;
00721
00722 if (Station::IsValidID(this->last_station_visited)) {
00723 Station *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(0);
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)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00841 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00842
00843 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
00844 }
00845 }
00846
00847
00848 v->OnNewDay();
00849 }
00850 }
00851
00852 void CallVehicleTicks()
00853 {
00854 _vehicles_to_autoreplace.Clear();
00855
00856 RunVehicleDayProc();
00857
00858 Station *st;
00859 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00860
00861 Vehicle *v;
00862 FOR_ALL_VEHICLES(v) {
00863
00864 if (!v->Tick()) {
00865 assert(Vehicle::Get(vehicle_index) == NULL);
00866 continue;
00867 }
00868
00869 assert(Vehicle::Get(vehicle_index) == v);
00870
00871 switch (v->type) {
00872 default: break;
00873
00874 case VEH_TRAIN:
00875 case VEH_ROAD:
00876 case VEH_AIRCRAFT:
00877 case VEH_SHIP:
00878 if (v->vcache.cached_cargo_age_period != 0) {
00879 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
00880 if (--v->cargo_age_counter == 0) {
00881 v->cargo.AgeCargo();
00882 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
00883 }
00884 }
00885
00886 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00887 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00888 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00889
00890 v->motion_counter += v->cur_speed;
00891
00892 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00893
00894
00895 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00896 }
00897 }
00898
00899 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00900 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00901 v = it->first;
00902
00903 cur_company.Change(v->owner);
00904
00905
00906
00907
00908 if (it->second) v->vehstatus &= ~VS_STOPPED;
00909
00910
00911 int x = v->x_pos;
00912 int y = v->y_pos;
00913 int z = v->z_pos;
00914
00915 const Company *c = Company::Get(_current_company);
00916 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00917 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00918 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00919
00920 if (!IsLocalCompany()) continue;
00921
00922 if (res.Succeeded()) {
00923 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00924 continue;
00925 }
00926
00927 StringID error_message = res.GetErrorMessage();
00928 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00929
00930 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00931
00932 StringID message;
00933 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00934 message = error_message;
00935 } else {
00936 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00937 }
00938
00939 SetDParam(0, v->index);
00940 SetDParam(1, error_message);
00941 AddVehicleAdviceNewsItem(message, v->index);
00942 }
00943
00944 cur_company.Restore();
00945 }
00946
00951 static void DoDrawVehicle(const Vehicle *v)
00952 {
00953 SpriteID image = v->cur_image;
00954 PaletteID pal = PAL_NONE;
00955
00956 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00957
00958
00959 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
00960
00961 if (v->type == VEH_EFFECT) {
00962
00963
00964 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00965 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00966 }
00967
00968 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00969 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
00970 }
00971
00976 void ViewportAddVehicles(DrawPixelInfo *dpi)
00977 {
00978
00979 const int l = dpi->left;
00980 const int r = dpi->left + dpi->width;
00981 const int t = dpi->top;
00982 const int b = dpi->top + dpi->height;
00983
00984
00985 int xl, xu, yl, yu;
00986
00987 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
00988 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
00989 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
00990 } else {
00991
00992 xl = 0;
00993 xu = 0x3F;
00994 }
00995
00996 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
00997 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
00998 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
00999 } else {
01000
01001 yl = 0;
01002 yu = 0x3F << 6;
01003 }
01004
01005 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
01006 for (int x = xl;; x = (x + 1) & 0x3F) {
01007 const Vehicle *v = _vehicle_viewport_hash[x + y];
01008
01009 while (v != NULL) {
01010 if (!(v->vehstatus & VS_HIDDEN) &&
01011 l <= v->coord.right &&
01012 t <= v->coord.bottom &&
01013 r >= v->coord.left &&
01014 b >= v->coord.top) {
01015 DoDrawVehicle(v);
01016 }
01017 v = v->hash_viewport_next;
01018 }
01019
01020 if (x == xu) break;
01021 }
01022
01023 if (y == yu) break;
01024 }
01025 }
01026
01034 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01035 {
01036 Vehicle *found = NULL, *v;
01037 uint dist, best_dist = UINT_MAX;
01038
01039 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01040
01041 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01042 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01043
01044 FOR_ALL_VEHICLES(v) {
01045 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
01046 x >= v->coord.left && x <= v->coord.right &&
01047 y >= v->coord.top && y <= v->coord.bottom) {
01048
01049 dist = max(
01050 abs(((v->coord.left + v->coord.right) >> 1) - x),
01051 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01052 );
01053
01054 if (dist < best_dist) {
01055 found = v;
01056 best_dist = dist;
01057 }
01058 }
01059 }
01060
01061 return found;
01062 }
01063
01068 void DecreaseVehicleValue(Vehicle *v)
01069 {
01070 v->value -= v->value >> 8;
01071 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01072 }
01073
01074 static const byte _breakdown_chance[64] = {
01075 3, 3, 3, 3, 3, 3, 3, 3,
01076 4, 4, 5, 5, 6, 6, 7, 7,
01077 8, 8, 9, 9, 10, 10, 11, 11,
01078 12, 13, 13, 13, 13, 14, 15, 16,
01079 17, 19, 21, 25, 28, 31, 34, 37,
01080 40, 44, 48, 52, 56, 60, 64, 68,
01081 72, 80, 90, 100, 110, 120, 130, 140,
01082 150, 170, 190, 210, 230, 250, 250, 250,
01083 };
01084
01085 void CheckVehicleBreakdown(Vehicle *v)
01086 {
01087 int rel, rel_old;
01088
01089
01090 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01091 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01092
01093 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01094 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01095 v->cur_speed < 5 || _game_mode == GM_MENU) {
01096 return;
01097 }
01098
01099 uint32 r = Random();
01100
01101
01102 int chance = v->breakdown_chance + 1;
01103 if (Chance16I(1, 25, r)) chance += 25;
01104 v->breakdown_chance = min(255, chance);
01105
01106
01107 rel = v->reliability;
01108 if (v->type == VEH_SHIP) rel += 0x6666;
01109
01110
01111 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01112
01113
01114 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01115 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01116 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01117 v->breakdown_chance = 0;
01118 }
01119 }
01120
01127 bool Vehicle::HandleBreakdown()
01128 {
01129
01130
01131
01132
01133
01134 switch (this->breakdown_ctr) {
01135 case 0:
01136 return false;
01137
01138 case 2:
01139 this->breakdown_ctr = 1;
01140
01141 if (this->breakdowns_since_last_service != 255) {
01142 this->breakdowns_since_last_service++;
01143 }
01144
01145 if (this->type == VEH_AIRCRAFT) {
01146
01147 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01148 } else {
01149 this->cur_speed = 0;
01150
01151 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01152 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01153 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01154 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01155 }
01156
01157 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
01158 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01159 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01160 }
01161 }
01162
01163 this->MarkDirty();
01164 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01165 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01166
01167
01168 case 1:
01169
01170 if (this->type == VEH_AIRCRAFT) return false;
01171
01172
01173 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01174 if (--this->breakdown_delay == 0) {
01175 this->breakdown_ctr = 0;
01176 this->MarkDirty();
01177 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01178 }
01179 }
01180 return true;
01181
01182 default:
01183 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01184 return false;
01185 }
01186 }
01187
01192 void AgeVehicle(Vehicle *v)
01193 {
01194 if (v->age < MAX_DAY) {
01195 v->age++;
01196 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
01197 }
01198
01199 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
01200
01201 int age = v->age - v->max_age;
01202 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01203 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01204 v->reliability_spd_dec <<= 1;
01205 }
01206
01207 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01208
01209
01210 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01211
01212
01213 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
01214
01215 StringID str;
01216 if (age == -DAYS_IN_LEAP_YEAR) {
01217 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01218 } else if (age == 0) {
01219 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01220 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01221 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01222 } else {
01223 return;
01224 }
01225
01226 SetDParam(0, v->index);
01227 AddVehicleAdviceNewsItem(str, v->index);
01228 }
01229
01236 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
01237 {
01238 int count = 0;
01239 int max = 0;
01240 int cars = 0;
01241 int unloading = 0;
01242 bool loading = false;
01243
01244 bool is_loading = front->current_order.IsType(OT_LOADING);
01245
01246
01247 const Station *st = Station::GetIfValid(front->last_station_visited);
01248 assert(colour == NULL || (st != NULL && is_loading));
01249
01250 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
01251 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
01252
01253
01254 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
01255 count += v->cargo.OnboardCount();
01256 max += v->cargo_cap;
01257 if (v->cargo_cap != 0 && colour != NULL) {
01258 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01259 loading |= !order_no_load &&
01260 (order_full_load || HasBit(st->goods[v->cargo_type].acceptance_pickup, GoodsEntry::GES_PICKUP)) &&
01261 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
01262 cars++;
01263 }
01264 }
01265
01266 if (colour != NULL) {
01267 if (unloading == 0 && loading) {
01268 *colour = STR_PERCENT_UP;
01269 } else if (unloading == 0 && !loading) {
01270 *colour = STR_PERCENT_NONE;
01271 } else if (cars == unloading || !loading) {
01272 *colour = STR_PERCENT_DOWN;
01273 } else {
01274 *colour = STR_PERCENT_UP_DOWN;
01275 }
01276 }
01277
01278
01279 if (max == 0) return 100;
01280
01281
01282 return (count * 100) / max;
01283 }
01284
01289 void VehicleEnterDepot(Vehicle *v)
01290 {
01291
01292 assert(v == v->First());
01293
01294 switch (v->type) {
01295 case VEH_TRAIN: {
01296 Train *t = Train::From(v);
01297 SetWindowClassesDirty(WC_TRAINS_LIST);
01298
01299 SetDepotReservation(t->tile, false);
01300 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01301
01302 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01303 t->wait_counter = 0;
01304 t->force_proceed = TFP_NONE;
01305 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01306 t->ConsistChanged(true);
01307 break;
01308 }
01309
01310 case VEH_ROAD:
01311 SetWindowClassesDirty(WC_ROADVEH_LIST);
01312 break;
01313
01314 case VEH_SHIP: {
01315 SetWindowClassesDirty(WC_SHIPS_LIST);
01316 Ship *ship = Ship::From(v);
01317 ship->state = TRACK_BIT_DEPOT;
01318 ship->UpdateCache();
01319 ship->UpdateViewport(true, true);
01320 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01321 break;
01322 }
01323
01324 case VEH_AIRCRAFT:
01325 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01326 HandleAircraftEnterHangar(Aircraft::From(v));
01327 break;
01328 default: NOT_REACHED();
01329 }
01330 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01331
01332 if (v->type != VEH_TRAIN) {
01333
01334
01335 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01336 }
01337 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01338
01339 v->vehstatus |= VS_HIDDEN;
01340 v->cur_speed = 0;
01341
01342 VehicleServiceInDepot(v);
01343
01344 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01345
01346 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01347 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01348
01349 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01350 Order t = v->current_order;
01351 v->current_order.MakeDummy();
01352
01353
01354
01355 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01356 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01357 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01358
01359 return;
01360 }
01361
01362 if (t.IsRefit()) {
01363 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01364 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01365 cur_company.Restore();
01366
01367 if (cost.Failed()) {
01368 _vehicles_to_autoreplace[v] = false;
01369 if (v->owner == _local_company) {
01370
01371 SetDParam(0, v->index);
01372 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
01373 }
01374 } else if (cost.GetCost() != 0) {
01375 v->profit_this_year -= cost.GetCost() << 8;
01376 if (v->owner == _local_company) {
01377 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01378 }
01379 }
01380 }
01381
01382 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01383
01384 v->DeleteUnreachedImplicitOrders();
01385 UpdateVehicleTimetable(v, true);
01386 v->IncrementImplicitOrderIndex();
01387 }
01388 if (t.GetDepotActionType() & ODATFB_HALT) {
01389
01390 _vehicles_to_autoreplace[v] = false;
01391 if (v->owner == _local_company) {
01392 SetDParam(0, v->index);
01393 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
01394 }
01395 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01396 }
01397 }
01398 }
01399
01400
01406 void VehicleUpdatePosition(Vehicle *v)
01407 {
01408 UpdateVehicleTileHash(v, false);
01409 }
01410
01417 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01418 {
01419 int img = v->cur_image;
01420 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01421 const Sprite *spr = GetSprite(img, ST_NORMAL);
01422
01423 pt.x += spr->x_offs;
01424 pt.y += spr->y_offs;
01425
01426 UpdateVehicleViewportHash(v, pt.x, pt.y);
01427
01428 Rect old_coord = v->coord;
01429 v->coord.left = pt.x;
01430 v->coord.top = pt.y;
01431 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01432 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01433
01434 if (dirty) {
01435 if (old_coord.left == INVALID_COORD) {
01436 MarkSingleVehicleDirty(v);
01437 } else {
01438 MarkAllViewportsDirty(
01439 min(old_coord.left, v->coord.left),
01440 min(old_coord.top, v->coord.top),
01441 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01442 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01443 );
01444 }
01445 }
01446 }
01447
01452 void VehicleUpdatePositionAndViewport(Vehicle *v)
01453 {
01454 VehicleUpdatePosition(v);
01455 VehicleUpdateViewport(v, true);
01456 }
01457
01462 void MarkSingleVehicleDirty(const Vehicle *v)
01463 {
01464 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01465 }
01466
01472 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01473 {
01474 static const int8 _delta_coord[16] = {
01475 -1,-1,-1, 0, 1, 1, 1, 0,
01476 -1, 0, 1, 1, 1, 0,-1,-1,
01477 };
01478
01479 int x = v->x_pos + _delta_coord[v->direction];
01480 int y = v->y_pos + _delta_coord[v->direction + 8];
01481
01482 GetNewVehiclePosResult gp;
01483 gp.x = x;
01484 gp.y = y;
01485 gp.old_tile = v->tile;
01486 gp.new_tile = TileVirtXY(x, y);
01487 return gp;
01488 }
01489
01490 static const Direction _new_direction_table[] = {
01491 DIR_N, DIR_NW, DIR_W,
01492 DIR_NE, DIR_SE, DIR_SW,
01493 DIR_E, DIR_SE, DIR_S
01494 };
01495
01496 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01497 {
01498 int i = 0;
01499
01500 if (y >= v->y_pos) {
01501 if (y != v->y_pos) i += 3;
01502 i += 3;
01503 }
01504
01505 if (x >= v->x_pos) {
01506 if (x != v->x_pos) i++;
01507 i++;
01508 }
01509
01510 Direction dir = v->direction;
01511
01512 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01513 if (dirdiff == DIRDIFF_SAME) return dir;
01514 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01515 }
01516
01526 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01527 {
01528 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01529 }
01530
01538 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01539 {
01540
01541 const Vehicle *v;
01542 FOR_ALL_VEHICLES(v) {
01543 if (v->type == type && v->owner == owner) {
01544 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01545 }
01546 }
01547
01548 if (this->maxid == 0) return;
01549
01550
01551
01552
01553 this->cache = CallocT<bool>(this->maxid + 2);
01554
01555
01556 FOR_ALL_VEHICLES(v) {
01557 if (v->type == type && v->owner == owner) {
01558 this->cache[v->unitnumber] = true;
01559 }
01560 }
01561 }
01562
01564 UnitID FreeUnitIDGenerator::NextID()
01565 {
01566 if (this->maxid <= this->curid) return ++this->curid;
01567
01568 while (this->cache[++this->curid]) { }
01569
01570 return this->curid;
01571 }
01572
01578 UnitID GetFreeUnitNumber(VehicleType type)
01579 {
01580
01581 uint max_veh;
01582 switch (type) {
01583 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01584 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01585 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01586 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01587 default: NOT_REACHED();
01588 }
01589
01590 const Company *c = Company::Get(_current_company);
01591 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01592
01593 FreeUnitIDGenerator gen(type, _current_company);
01594
01595 return gen.NextID();
01596 }
01597
01598
01607 bool CanBuildVehicleInfrastructure(VehicleType type)
01608 {
01609 assert(IsCompanyBuildableVehicleType(type));
01610
01611 if (!Company::IsValidID(_local_company)) return false;
01612 if (!_settings_client.gui.disable_unsuitable_building) return true;
01613
01614 UnitID max;
01615 switch (type) {
01616 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01617 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01618 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01619 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01620 default: NOT_REACHED();
01621 }
01622
01623
01624 if (max > 0) {
01625
01626 const Engine *e;
01627 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01628 if (HasBit(e->company_avail, _local_company)) return true;
01629 }
01630 return false;
01631 }
01632
01633
01634 const Vehicle *v;
01635 FOR_ALL_VEHICLES(v) {
01636 if (v->owner == _local_company && v->type == type) return true;
01637 }
01638
01639 return false;
01640 }
01641
01642
01650 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01651 {
01652 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01653 const Engine *e = Engine::Get(engine_type);
01654 switch (e->type) {
01655 default: NOT_REACHED();
01656 case VEH_TRAIN:
01657 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01658
01659
01660 engine_type = parent_engine_type;
01661 e = Engine::Get(engine_type);
01662
01663 }
01664
01665 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01666 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01667 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01668 if (!CargoSpec::Get(cargo_type)->is_freight) {
01669 if (parent_engine_type == INVALID_ENGINE) {
01670 return LS_PASSENGER_WAGON_STEAM;
01671 } else {
01672 switch (RailVehInfo(parent_engine_type)->engclass) {
01673 default: NOT_REACHED();
01674 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01675 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01676 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01677 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01678 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01679 }
01680 }
01681 } else {
01682 return LS_FREIGHT_WAGON;
01683 }
01684 } else {
01685 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01686
01687 switch (e->u.rail.engclass) {
01688 default: NOT_REACHED();
01689 case EC_STEAM: return LS_STEAM;
01690 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01691 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01692 case EC_MONORAIL: return LS_MONORAIL;
01693 case EC_MAGLEV: return LS_MAGLEV;
01694 }
01695 }
01696
01697 case VEH_ROAD:
01698
01699 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01700 engine_type = parent_engine_type;
01701 e = Engine::Get(engine_type);
01702 cargo_type = v->First()->cargo_type;
01703 }
01704 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01705 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01706
01707
01708 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01709
01710 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01711 } else {
01712
01713 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01714 }
01715
01716 case VEH_SHIP:
01717 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01718 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01719 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01720
01721 case VEH_AIRCRAFT:
01722 switch (e->u.air.subtype) {
01723 case AIR_HELI: return LS_HELICOPTER;
01724 case AIR_CTOL: return LS_SMALL_PLANE;
01725 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01726 default: NOT_REACHED();
01727 }
01728 }
01729 }
01730
01740 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01741 {
01742 const Company *c = Company::Get(company);
01743 LiveryScheme scheme = LS_DEFAULT;
01744
01745
01746
01747 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01748
01749 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01750
01751
01752 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01753 }
01754
01755 return &c->livery[scheme];
01756 }
01757
01758
01759 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01760 {
01761 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01762
01763
01764 if (map != PAL_NONE) return map;
01765
01766 const Engine *e = Engine::Get(engine_type);
01767
01768
01769 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01770 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01771
01772 if (callback != CALLBACK_FAILED) {
01773 assert_compile(PAL_NONE == 0);
01774 map = GB(callback, 0, 14);
01775
01776
01777 if (!HasBit(callback, 14)) {
01778
01779 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01780 return map;
01781 }
01782 }
01783 }
01784
01785 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01786
01787 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01788
01789
01790 if (!Company::IsValidID(company)) return map;
01791
01792 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01793
01794 map += livery->colour1;
01795 if (twocc) map += livery->colour2 * 16;
01796
01797
01798 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01799 return map;
01800 }
01801
01808 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01809 {
01810 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01811 }
01812
01818 PaletteID GetVehiclePalette(const Vehicle *v)
01819 {
01820 if (v->IsGroundVehicle()) {
01821 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01822 }
01823
01824 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01825 }
01826
01830 void Vehicle::DeleteUnreachedImplicitOrders()
01831 {
01832 if (this->IsGroundVehicle()) {
01833 uint16 &gv_flags = this->GetGroundVehicleFlags();
01834 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01835
01836 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01837 this->cur_implicit_order_index = this->cur_real_order_index;
01838 InvalidateVehicleOrder(this, 0);
01839 return;
01840 }
01841 }
01842
01843 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01844 while (order != NULL) {
01845 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01846
01847 if (order->IsType(OT_IMPLICIT)) {
01848
01849 order = order->next;
01850 DeleteOrder(this, this->cur_implicit_order_index);
01851 } else {
01852
01853 order = order->next;
01854 this->cur_implicit_order_index++;
01855 }
01856
01857
01858 if (order == NULL) {
01859 order = this->GetOrder(0);
01860 this->cur_implicit_order_index = 0;
01861 }
01862 }
01863 }
01864
01869 void Vehicle::BeginLoading()
01870 {
01871 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01872
01873 if (this->current_order.IsType(OT_GOTO_STATION) &&
01874 this->current_order.GetDestination() == this->last_station_visited) {
01875 this->DeleteUnreachedImplicitOrders();
01876
01877
01878 this->current_order.MakeLoading(true);
01879 UpdateVehicleTimetable(this, true);
01880
01881
01882
01883
01884
01885
01886 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01887
01888 } else {
01889
01890
01891
01892
01893
01894 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01895 if (this->IsGroundVehicle() && in_list != NULL &&
01896 (!in_list->IsType(OT_IMPLICIT) ||
01897 in_list->GetDestination() != this->last_station_visited)) {
01898 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01899
01900 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01901 if (prev_order == NULL ||
01902 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01903 prev_order->GetDestination() != this->last_station_visited) {
01904
01905
01906
01907 int target_index = this->cur_implicit_order_index;
01908 bool found = false;
01909 while (target_index != this->cur_real_order_index) {
01910 const Order *order = this->GetOrder(target_index);
01911 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01912 found = true;
01913 break;
01914 }
01915 target_index++;
01916 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01917 assert(target_index != this->cur_implicit_order_index);
01918 }
01919
01920 if (found) {
01921 if (suppress_implicit_orders) {
01922
01923 this->cur_implicit_order_index = target_index;
01924 InvalidateVehicleOrder(this, 0);
01925 } else {
01926
01927 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01928 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01929 if (order->IsType(OT_IMPLICIT)) {
01930
01931 order = order->next;
01932 DeleteOrder(this, this->cur_implicit_order_index);
01933 } else {
01934
01935 order = order->next;
01936 this->cur_implicit_order_index++;
01937 }
01938
01939
01940 if (order == NULL) {
01941 order = this->GetOrder(0);
01942 this->cur_implicit_order_index = 0;
01943 }
01944 assert(order != NULL);
01945 }
01946 }
01947 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01948
01949 Order *implicit_order = new Order();
01950 implicit_order->MakeImplicit(this->last_station_visited);
01951 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01952 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01953
01954
01955
01956 uint16 &gv_flags = this->GetGroundVehicleFlags();
01957 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01958 }
01959 }
01960 }
01961 this->current_order.MakeLoading(false);
01962 }
01963
01964 if (this->last_loading_station != INVALID_STATION &&
01965 this->last_loading_station != this->last_station_visited &&
01966 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
01967 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
01968 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
01969 }
01970
01971 PrepareUnload(this);
01972
01973 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01974 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
01975 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01976 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01977
01978 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01979 this->cur_speed = 0;
01980 this->MarkDirty();
01981 }
01982
01987 void Vehicle::CancelReservation(StationID next, Station *st)
01988 {
01989 for (Vehicle *v = this; v != NULL; v = v->next) {
01990 VehicleCargoList &cargo = v->cargo;
01991 if (cargo.ReservedCount() > 0) {
01992 DEBUG(misc, 1, "cancelling cargo reservation");
01993 GoodsEntry &ge = st->goods[v->cargo_type];
01994 cargo.Unreserve(next, &ge.cargo);
01995 SetBit(ge.acceptance_pickup, GoodsEntry::GES_PICKUP);
01996 }
01997 }
01998 }
01999
02004 void Vehicle::LeaveStation()
02005 {
02006 assert(this->current_order.IsType(OT_LOADING));
02007
02008 delete this->cargo_payment;
02009
02010
02011 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
02012
02013 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
02014 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
02015 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
02016
02017
02018
02019 this->RefreshNextHopsStats();
02020
02021
02022 this->last_loading_station = this->last_station_visited;
02023 } else {
02024
02025
02026 this->last_loading_station = INVALID_STATION;
02027 }
02028 }
02029
02030 this->current_order.MakeLeaveStation();
02031 Station *st = Station::Get(this->last_station_visited);
02032 this->CancelReservation(INVALID_STATION, st);
02033 st->loading_vehicles.remove(this);
02034
02035 HideFillingPercent(&this->fill_percent_te_id);
02036
02037 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02038
02039 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02040
02041 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02042 }
02043 }
02044
02051 void Vehicle::RefreshNextHopsStats()
02052 {
02053
02054 SmallMap<CargoID, uint, 1> capacities;
02055 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02056 v->refit_cap = v->cargo_cap;
02057 if (v->refit_cap == 0) continue;
02058 SmallPair<CargoID, uint> *i = capacities.Find(v->cargo_type);
02059 if (i == capacities.End()) {
02060
02061 i = capacities.Append();
02062 i->first = v->cargo_type;
02063 i->second = v->cargo_cap;
02064 } else {
02065 i->second += v->cargo_cap;
02066 }
02067 }
02068
02069
02070 if (this->orders.list == NULL) return;
02071
02072 uint hops = 0;
02073 const Order *first = this->orders.list->GetNextStoppingOrder(this,
02074 this->GetOrder(this->cur_implicit_order_index), hops);
02075 const Order *cur = first;
02076 const Order *next = first;
02077 while (next != NULL && cur->CanLeaveWithCargo(true)) {
02078 next = this->orders.list->GetNextStoppingOrder(this,
02079 this->orders.list->GetNext(next), ++hops);
02080 if (next == NULL) break;
02081
02082 if (next->IsType(OT_GOTO_DEPOT)) {
02083
02084 CargoID new_cid = next->GetRefitCargo();
02085 byte new_subtype = next->GetRefitSubtype();
02086 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02087 const Engine *e = Engine::Get(v->engine_type);
02088 if (!HasBit(e->info.refit_mask, new_cid)) continue;
02089
02090
02091 CargoID temp_cid = v->cargo_type;
02092 byte temp_subtype = v->cargo_subtype;
02093 v->cargo_type = new_cid;
02094 v->cargo_subtype = new_subtype;
02095
02096 uint16 mail_capacity = 0;
02097 uint amount = e->DetermineCapacity(v, &mail_capacity);
02098
02099
02100 v->cargo_type = temp_cid;
02101 v->cargo_subtype = temp_subtype;
02102
02103
02104 if (new_cid != v->cargo_type && v->refit_cap > 0) {
02105 capacities[v->cargo_type] -= v->refit_cap;
02106 v->refit_cap = 0;
02107 } else if (amount < v->refit_cap) {
02108 capacities[v->cargo_type] -= v->refit_cap - amount;
02109 v->refit_cap = amount;
02110 }
02111
02112
02113 if (v->type == VEH_AIRCRAFT) {
02114 Vehicle *u = v->Next();
02115 if (mail_capacity < u->refit_cap) {
02116 capacities[u->cargo_type] -= u->refit_cap - mail_capacity;
02117 u->refit_cap = mail_capacity;
02118 }
02119 break;
02120 }
02121 if (v->type == VEH_SHIP) break;
02122 }
02123 } else {
02124 StationID next_station = next->GetDestination();
02125 Station *st = Station::GetIfValid(cur->GetDestination());
02126 if (st != NULL && next_station != INVALID_STATION && next_station != st->index) {
02127 for (const SmallPair<CargoID, uint> *i = capacities.Begin(); i != capacities.End(); ++i) {
02128
02129 if (i->second > 0) IncreaseStats(st, i->first, next_station, i->second, UINT_MAX);
02130 }
02131 }
02132 cur = next;
02133 if (cur == first) break;
02134 }
02135 }
02136
02137 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
02138 }
02139
02145 void Vehicle::HandleLoading(bool mode)
02146 {
02147 switch (this->current_order.GetType()) {
02148 case OT_LOADING: {
02149 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02150
02151
02152 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02153
02154 this->PlayLeaveStationSound();
02155
02156 this->LeaveStation();
02157
02158
02159 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02160 if (order == NULL ||
02161 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02162 order->GetDestination() != this->last_station_visited) {
02163 return;
02164 }
02165 break;
02166 }
02167
02168 case OT_DUMMY: break;
02169
02170 default: return;
02171 }
02172
02173 this->IncrementImplicitOrderIndex();
02174 }
02175
02180 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
02181 {
02182 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02183 if (v->cargo_cap == 0) continue;
02184 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
02185 if (pair == capacities.End()) {
02186 pair = capacities.Append();
02187 pair->first = v->cargo_type;
02188 pair->second = v->cargo_cap - v->cargo.Count();
02189 } else {
02190 pair->second += v->cargo_cap - v->cargo.Count();
02191 }
02192 }
02193 }
02194
02195 uint Vehicle::GetConsistTotalCapacity() const
02196 {
02197 uint result = 0;
02198 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02199 result += v->cargo_cap;
02200 }
02201 return result;
02202 }
02203
02210 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02211 {
02212 CommandCost ret = CheckOwnership(this->owner);
02213 if (ret.Failed()) return ret;
02214
02215 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02216 if (this->IsStoppedInDepot()) return CMD_ERROR;
02217
02218 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02219 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02220 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02221
02222
02223
02224 if (flags & DC_EXEC) {
02225 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02226 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02227 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02228 }
02229 return CommandCost();
02230 }
02231
02232 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02233 if (flags & DC_EXEC) {
02234
02235
02236 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02237
02238 if (this->IsGroundVehicle()) {
02239 uint16 &gv_flags = this->GetGroundVehicleFlags();
02240 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02241 }
02242
02243 this->current_order.MakeDummy();
02244 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02245 }
02246 return CommandCost();
02247 }
02248
02249 TileIndex location;
02250 DestinationID destination;
02251 bool reverse;
02252 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};
02253 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02254
02255 if (flags & DC_EXEC) {
02256 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02257
02258 if (this->IsGroundVehicle()) {
02259 uint16 &gv_flags = this->GetGroundVehicleFlags();
02260 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02261 }
02262
02263 this->dest_tile = location;
02264 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02265 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02266 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02267
02268
02269 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02270
02271 if (this->type == VEH_AIRCRAFT) {
02272 Aircraft *a = Aircraft::From(this);
02273 if (a->state == FLYING && a->targetairport != destination) {
02274
02275 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02276 AircraftNextAirportPos_and_Order(a);
02277 }
02278 }
02279 }
02280
02281 return CommandCost();
02282
02283 }
02284
02289 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02290 {
02291 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02292 const Engine *e = this->GetEngine();
02293
02294
02295 byte visual_effect;
02296 switch (e->type) {
02297 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02298 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02299 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02300 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02301 }
02302
02303
02304 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02305 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02306
02307 if (callback != CALLBACK_FAILED) {
02308 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02309
02310 callback = GB(callback, 0, 8);
02311
02312
02313 if (callback == VE_DEFAULT) {
02314 assert(HasBit(callback, VE_DISABLE_EFFECT));
02315 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02316 }
02317 visual_effect = callback;
02318 }
02319 }
02320
02321
02322 if (visual_effect == VE_DEFAULT ||
02323 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02324
02325
02326 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02327 if (visual_effect == VE_DEFAULT) {
02328 visual_effect = 1 << VE_DISABLE_EFFECT;
02329 } else {
02330 SetBit(visual_effect, VE_DISABLE_EFFECT);
02331 }
02332 } else {
02333 if (visual_effect == VE_DEFAULT) {
02334
02335 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02336 }
02337 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02338 }
02339 }
02340
02341 this->vcache.cached_vis_effect = visual_effect;
02342
02343 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02344 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02345 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02346 }
02347 }
02348
02349 static const int8 _vehicle_smoke_pos[8] = {
02350 1, 1, 1, 0, -1, -1, -1, 0
02351 };
02352
02357 void Vehicle::ShowVisualEffect() const
02358 {
02359 assert(this->IsPrimaryVehicle());
02360 bool sound = false;
02361
02362
02363
02364
02365
02366
02367 if (_settings_game.vehicle.smoke_amount == 0 ||
02368 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02369 this->cur_speed < 2) {
02370 return;
02371 }
02372
02373 uint max_speed = this->vcache.cached_max_speed;
02374 if (this->type == VEH_TRAIN) {
02375 const Train *t = Train::From(this);
02376
02377
02378
02379
02380 if (HasBit(t->flags, VRF_REVERSING) ||
02381 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02382 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02383 return;
02384 }
02385
02386 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02387 max_speed = min(max_speed, this->current_order.max_speed);
02388 }
02389 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02390
02391 const Vehicle *v = this;
02392
02393 do {
02394 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02395 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02396 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02397
02398
02399
02400
02401
02402
02403
02404
02405 if (disable_effect ||
02406 v->vehstatus & VS_HIDDEN ||
02407 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02408 IsDepotTile(v->tile) ||
02409 IsTunnelTile(v->tile) ||
02410 (v->type == VEH_TRAIN &&
02411 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02412 continue;
02413 }
02414
02415
02416
02417
02418 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02419
02420 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02421 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02422
02423 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02424 x = -x;
02425 y = -y;
02426 }
02427
02428 switch (effect_type) {
02429 case VE_TYPE_STEAM:
02430
02431
02432
02433
02434
02435 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02436 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02437 sound = true;
02438 }
02439 break;
02440
02441 case VE_TYPE_DIESEL: {
02442
02443
02444
02445
02446
02447
02448
02449
02450
02451
02452
02453 int power_weight_effect = 0;
02454 if (v->type == VEH_TRAIN) {
02455 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02456 }
02457 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02458 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02459 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02460 sound = true;
02461 }
02462 break;
02463 }
02464
02465 case VE_TYPE_ELECTRIC:
02466
02467
02468
02469
02470
02471
02472 if (GB(v->tick_counter, 0, 2) == 0 &&
02473 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02474 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02475 sound = true;
02476 }
02477 break;
02478
02479 default:
02480 break;
02481 }
02482 } while ((v = v->Next()) != NULL);
02483
02484 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02485 }
02486
02491 void Vehicle::SetNext(Vehicle *next)
02492 {
02493 assert(this != next);
02494
02495 if (this->next != NULL) {
02496
02497 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02498 v->first = this->next;
02499 }
02500 this->next->previous = NULL;
02501 }
02502
02503 this->next = next;
02504
02505 if (this->next != NULL) {
02506
02507 if (this->next->previous != NULL) this->next->previous->next = NULL;
02508 this->next->previous = this;
02509 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02510 v->first = this->first;
02511 }
02512 }
02513 }
02514
02520 void Vehicle::AddToShared(Vehicle *shared_chain)
02521 {
02522 assert(this->previous_shared == NULL && this->next_shared == NULL);
02523
02524 if (shared_chain->orders.list == NULL) {
02525 assert(shared_chain->previous_shared == NULL);
02526 assert(shared_chain->next_shared == NULL);
02527 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02528 }
02529
02530 this->next_shared = shared_chain->next_shared;
02531 this->previous_shared = shared_chain;
02532
02533 shared_chain->next_shared = this;
02534
02535 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02536
02537 shared_chain->orders.list->AddVehicle(this);
02538 }
02539
02543 void Vehicle::RemoveFromShared()
02544 {
02545
02546
02547 bool were_first = (this->FirstShared() == this);
02548 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02549
02550 this->orders.list->RemoveVehicle(this);
02551
02552 if (!were_first) {
02553
02554 this->previous_shared->next_shared = this->NextShared();
02555 }
02556
02557 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02558
02559
02560 if (this->orders.list->GetNumVehicles() == 1) {
02561
02562 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02563 InvalidateVehicleOrder(this->FirstShared(), 0);
02564 } else if (were_first) {
02565
02566
02567 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02568 }
02569
02570 this->next_shared = NULL;
02571 this->previous_shared = NULL;
02572 }
02573
02574 void VehiclesYearlyLoop()
02575 {
02576 Vehicle *v;
02577 FOR_ALL_VEHICLES(v) {
02578 if (v->IsPrimaryVehicle()) {
02579
02580 Money profit = v->GetDisplayProfitThisYear();
02581 if (v->age >= 730 && profit < 0) {
02582 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02583 SetDParam(0, v->index);
02584 SetDParam(1, profit);
02585 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
02586 }
02587 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02588 }
02589
02590 v->profit_last_year = v->profit_this_year;
02591 v->profit_this_year = 0;
02592 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02593 }
02594 }
02595 GroupStatistics::UpdateProfits();
02596 SetWindowClassesDirty(WC_TRAINS_LIST);
02597 SetWindowClassesDirty(WC_SHIPS_LIST);
02598 SetWindowClassesDirty(WC_ROADVEH_LIST);
02599 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02600 }
02601
02602
02612 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02613 {
02614 const Engine *e = Engine::GetIfValid(engine_type);
02615 assert(e != NULL);
02616
02617 switch (e->type) {
02618 case VEH_TRAIN:
02619 return (st->facilities & FACIL_TRAIN) != 0;
02620
02621 case VEH_ROAD:
02622
02623
02624
02625 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02626
02627 case VEH_SHIP:
02628 return (st->facilities & FACIL_DOCK) != 0;
02629
02630 case VEH_AIRCRAFT:
02631 return (st->facilities & FACIL_AIRPORT) != 0 &&
02632 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02633
02634 default:
02635 return false;
02636 }
02637 }
02638
02645 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02646 {
02647 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02648
02649 return CanVehicleUseStation(v->engine_type, st);
02650 }
02651
02657 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02658 {
02659 assert(this->IsGroundVehicle());
02660 if (this->type == VEH_TRAIN) {
02661 return &Train::From(this)->gcache;
02662 } else {
02663 return &RoadVehicle::From(this)->gcache;
02664 }
02665 }
02666
02672 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02673 {
02674 assert(this->IsGroundVehicle());
02675 if (this->type == VEH_TRAIN) {
02676 return &Train::From(this)->gcache;
02677 } else {
02678 return &RoadVehicle::From(this)->gcache;
02679 }
02680 }
02681
02687 uint16 &Vehicle::GetGroundVehicleFlags()
02688 {
02689 assert(this->IsGroundVehicle());
02690 if (this->type == VEH_TRAIN) {
02691 return Train::From(this)->gv_flags;
02692 } else {
02693 return RoadVehicle::From(this)->gv_flags;
02694 }
02695 }
02696
02702 const uint16 &Vehicle::GetGroundVehicleFlags() const
02703 {
02704 assert(this->IsGroundVehicle());
02705 if (this->type == VEH_TRAIN) {
02706 return Train::From(this)->gv_flags;
02707 } else {
02708 return RoadVehicle::From(this)->gv_flags;
02709 }
02710 }
02711
02720 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02721 {
02722 if (v->type == VEH_TRAIN) {
02723 Train *u = Train::From(v);
02724
02725 u = u->GetFirstEnginePart();
02726
02727
02728 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02729 do {
02730
02731 set.Include(u->index);
02732
02733
02734 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02735
02736 u = u->Next();
02737 } while (u != NULL && u->IsArticulatedPart());
02738 }
02739 }
02740 }