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 return RandomRange(pass + 1);
00204 }
00205
00206
00215 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00216 {
00217 const Engine *e = Engine::Get(engine);
00218 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
00219
00220 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00221 SetBit(grfconfig->grf_bugs, bug_type);
00222 SetDParamStr(0, grfconfig->GetName());
00223 SetDParam(1, engine);
00224 ShowErrorMessage(part1, part2, WL_CRITICAL);
00225 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00226 }
00227
00228
00229 char buffer[512];
00230
00231 SetDParamStr(0, grfconfig->GetName());
00232 GetString(buffer, part1, lastof(buffer));
00233 DEBUG(grf, 0, "%s", buffer + 3);
00234
00235 SetDParam(1, engine);
00236 GetString(buffer, part2, lastof(buffer));
00237 DEBUG(grf, 0, "%s", buffer + 3);
00238 }
00239
00245 void VehicleLengthChanged(const Vehicle *u)
00246 {
00247
00248 const Engine *engine = u->GetEngine();
00249 uint32 grfid = engine->grf_prop.grffile->grfid;
00250 GRFConfig *grfconfig = GetGRFConfig(grfid);
00251 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
00252 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
00253 }
00254 }
00255
00260 Vehicle::Vehicle(VehicleType type)
00261 {
00262 this->type = type;
00263 this->coord.left = INVALID_COORD;
00264 this->group_id = DEFAULT_GROUP;
00265 this->fill_percent_te_id = INVALID_TE_ID;
00266 this->first = this;
00267 this->colourmap = PAL_NONE;
00268 this->cargo_age_counter = 1;
00269 }
00270
00275 byte VehicleRandomBits()
00276 {
00277 return GB(Random(), 0, 8);
00278 }
00279
00280
00281
00282 const int HASH_BITS = 7;
00283 const int HASH_SIZE = 1 << HASH_BITS;
00284 const int HASH_MASK = HASH_SIZE - 1;
00285 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00286 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00287
00288
00289
00290 const int HASH_RES = 0;
00291
00292 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
00293
00294 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00295 {
00296 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00297 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00298 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00299 for (; v != NULL; v = v->hash_tile_next) {
00300 Vehicle *a = proc(v, data);
00301 if (find_first && a != NULL) return a;
00302 }
00303 if (x == xu) break;
00304 }
00305 if (y == yu) break;
00306 }
00307
00308 return NULL;
00309 }
00310
00311
00323 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00324 {
00325 const int COLL_DIST = 6;
00326
00327
00328 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00329 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00330 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00331 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00332
00333 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
00334 }
00335
00350 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00351 {
00352 VehicleFromPosXY(x, y, data, proc, false);
00353 }
00354
00366 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00367 {
00368 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00369 }
00370
00381 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00382 {
00383 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00384 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00385
00386 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00387 for (; v != NULL; v = v->hash_tile_next) {
00388 if (v->tile != tile) continue;
00389
00390 Vehicle *a = proc(v, data);
00391 if (find_first && a != NULL) return a;
00392 }
00393
00394 return NULL;
00395 }
00396
00410 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00411 {
00412 VehicleFromPos(tile, data, proc, false);
00413 }
00414
00425 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00426 {
00427 return VehicleFromPos(tile, data, proc, true) != NULL;
00428 }
00429
00436 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00437 {
00438 int z = *(int*)data;
00439
00440 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00441 if (v->z_pos > z) return NULL;
00442
00443 return v;
00444 }
00445
00451 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00452 {
00453 int z = GetTileMaxPixelZ(tile);
00454
00455
00456
00457
00458
00459 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00460 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00461 return CommandCost();
00462 }
00463
00465 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00466 {
00467 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00468 if (v == (const Vehicle *)data) return NULL;
00469
00470 return v;
00471 }
00472
00480 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00481 {
00482
00483
00484
00485
00486 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00487 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00488
00489 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00490 return CommandCost();
00491 }
00492
00493 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00494 {
00495 TrackBits rail_bits = *(TrackBits *)data;
00496
00497 if (v->type != VEH_TRAIN) return NULL;
00498
00499 Train *t = Train::From(v);
00500 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00501
00502 return v;
00503 }
00504
00513 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00514 {
00515
00516
00517
00518
00519 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00520 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00521 return CommandCost();
00522 }
00523
00524 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
00525 {
00526 Vehicle **old_hash = v->hash_tile_current;
00527 Vehicle **new_hash;
00528
00529 if (remove) {
00530 new_hash = NULL;
00531 } else {
00532 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00533 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00534 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00535 }
00536
00537 if (old_hash == new_hash) return;
00538
00539
00540 if (old_hash != NULL) {
00541 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
00542 *v->hash_tile_prev = v->hash_tile_next;
00543 }
00544
00545
00546 if (new_hash != NULL) {
00547 v->hash_tile_next = *new_hash;
00548 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
00549 v->hash_tile_prev = new_hash;
00550 *new_hash = v;
00551 }
00552
00553
00554 v->hash_tile_current = new_hash;
00555 }
00556
00557 static Vehicle *_vehicle_viewport_hash[0x1000];
00558
00559 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
00560 {
00561 Vehicle **old_hash, **new_hash;
00562 int old_x = v->coord.left;
00563 int old_y = v->coord.top;
00564
00565 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
00566 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
00567
00568 if (old_hash == new_hash) return;
00569
00570
00571 if (old_hash != NULL) {
00572 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
00573 *v->hash_viewport_prev = v->hash_viewport_next;
00574 }
00575
00576
00577 if (new_hash != NULL) {
00578 v->hash_viewport_next = *new_hash;
00579 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
00580 v->hash_viewport_prev = new_hash;
00581 *new_hash = v;
00582 }
00583 }
00584
00585 void ResetVehicleHash()
00586 {
00587 Vehicle *v;
00588 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
00589 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
00590 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
00591 }
00592
00593 void ResetVehicleColourMap()
00594 {
00595 Vehicle *v;
00596 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00597 }
00598
00603 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00604 static AutoreplaceMap _vehicles_to_autoreplace;
00605
00606 void InitializeVehicles()
00607 {
00608 _vehicles_to_autoreplace.Reset();
00609 ResetVehicleHash();
00610 }
00611
00612 uint CountVehiclesInChain(const Vehicle *v)
00613 {
00614 uint count = 0;
00615 do count++; while ((v = v->Next()) != NULL);
00616 return count;
00617 }
00618
00623 bool Vehicle::IsEngineCountable() const
00624 {
00625 switch (this->type) {
00626 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00627 case VEH_TRAIN:
00628 return !this->IsArticulatedPart() &&
00629 !Train::From(this)->IsRearDualheaded();
00630 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00631 case VEH_SHIP: return true;
00632 default: return false;
00633 }
00634 }
00635
00640 bool Vehicle::HasEngineType() const
00641 {
00642 switch (this->type) {
00643 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00644 case VEH_TRAIN:
00645 case VEH_ROAD:
00646 case VEH_SHIP: return true;
00647 default: return false;
00648 }
00649 }
00650
00656 const Engine *Vehicle::GetEngine() const
00657 {
00658 return Engine::Get(this->engine_type);
00659 }
00660
00666 const GRFFile *Vehicle::GetGRF() const
00667 {
00668 return this->GetEngine()->GetGRF();
00669 }
00670
00676 uint32 Vehicle::GetGRFID() const
00677 {
00678 return this->GetEngine()->GetGRFID();
00679 }
00680
00688 void Vehicle::HandlePathfindingResult(bool path_found)
00689 {
00690 if (path_found) {
00691
00692 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00693
00694
00695 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00696
00697 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00698 return;
00699 }
00700
00701
00702 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00703
00704
00705 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00706
00707 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
00708 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00709 SetDParam(0, this->index);
00710 AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00711 }
00712 }
00713
00715 void Vehicle::PreDestructor()
00716 {
00717 if (CleaningPool()) return;
00718
00719 if (Station::IsValidID(this->last_station_visited)) {
00720 Station *st = Station::Get(this->last_station_visited);
00721 st->loading_vehicles.remove(this);
00722
00723 HideFillingPercent(&this->fill_percent_te_id);
00724 this->CancelReservation(INVALID_STATION, st);
00725 delete this->cargo_payment;
00726 }
00727
00728 if (this->IsEngineCountable()) {
00729 GroupStatistics::CountEngine(this, -1);
00730 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
00731 GroupStatistics::UpdateAutoreplace(this->owner);
00732
00733 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00734 DeleteGroupHighlightOfVehicle(this);
00735 }
00736
00737 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00738 Aircraft *a = Aircraft::From(this);
00739 Station *st = GetTargetAirportIfValid(a);
00740 if (st != NULL) {
00741 const AirportFTA *layout = st->airport.GetFTA()->layout;
00742 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00743 }
00744 }
00745
00746
00747 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00748 RoadVehicle *v = RoadVehicle::From(this);
00749 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00750
00751 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00752 }
00753 }
00754
00755 if (this->Previous() == NULL) {
00756 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00757 }
00758
00759 if (this->IsPrimaryVehicle()) {
00760 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00761 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00762 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00763 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00764 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00765 SetWindowDirty(WC_COMPANY, this->owner);
00766 OrderBackup::ClearVehicle(this);
00767 }
00768 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00769
00770 this->cargo.Truncate(0);
00771 DeleteVehicleOrders(this);
00772 DeleteDepotHighlightOfVehicle(this);
00773
00774 extern void StopGlobalFollowVehicle(const Vehicle *v);
00775 StopGlobalFollowVehicle(this);
00776
00777 ReleaseDisastersTargetingVehicle(this->index);
00778 }
00779
00780 Vehicle::~Vehicle()
00781 {
00782 free(this->name);
00783
00784 if (CleaningPool()) {
00785 this->cargo.OnCleanPool();
00786 return;
00787 }
00788
00789
00790
00791 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00792
00793 Vehicle *v = this->Next();
00794 this->SetNext(NULL);
00795
00796 delete v;
00797
00798 UpdateVehicleTileHash(this, true);
00799 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
00800 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00801 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00802 }
00803
00808 void VehicleEnteredDepotThisTick(Vehicle *v)
00809 {
00810
00811 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00812
00813
00814
00815
00816
00817
00818 v->vehstatus |= VS_STOPPED;
00819 }
00820
00826 static void RunVehicleDayProc()
00827 {
00828 if (_game_mode != GM_NORMAL) return;
00829
00830
00831 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00832 Vehicle *v = Vehicle::Get(i);
00833 if (v == NULL) continue;
00834
00835
00836 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
00837 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00838 if (callback != CALLBACK_FAILED) {
00839 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00840 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00841
00842 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
00843 }
00844 }
00845
00846
00847 v->OnNewDay();
00848 }
00849 }
00850
00851 void CallVehicleTicks()
00852 {
00853 _vehicles_to_autoreplace.Clear();
00854
00855 RunVehicleDayProc();
00856
00857 Station *st;
00858 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00859
00860 Vehicle *v;
00861 FOR_ALL_VEHICLES(v) {
00862
00863 if (!v->Tick()) {
00864 assert(Vehicle::Get(vehicle_index) == NULL);
00865 continue;
00866 }
00867
00868 assert(Vehicle::Get(vehicle_index) == v);
00869
00870 switch (v->type) {
00871 default: break;
00872
00873 case VEH_TRAIN:
00874 case VEH_ROAD:
00875 case VEH_AIRCRAFT:
00876 case VEH_SHIP:
00877 if (v->vcache.cached_cargo_age_period != 0) {
00878 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
00879 if (--v->cargo_age_counter == 0) {
00880 v->cargo.AgeCargo();
00881 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
00882 }
00883 }
00884
00885 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00886 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00887 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00888
00889 v->motion_counter += v->cur_speed;
00890
00891 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00892
00893
00894 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00895 }
00896 }
00897
00898 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00899 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00900 v = it->first;
00901
00902 cur_company.Change(v->owner);
00903
00904
00905
00906
00907 if (it->second) v->vehstatus &= ~VS_STOPPED;
00908
00909
00910 int x = v->x_pos;
00911 int y = v->y_pos;
00912 int z = v->z_pos;
00913
00914 const Company *c = Company::Get(_current_company);
00915 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00916 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00917 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00918
00919 if (!IsLocalCompany()) continue;
00920
00921 if (res.Succeeded()) {
00922 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00923 continue;
00924 }
00925
00926 StringID error_message = res.GetErrorMessage();
00927 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00928
00929 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00930
00931 StringID message;
00932 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00933 message = error_message;
00934 } else {
00935 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00936 }
00937
00938 SetDParam(0, v->index);
00939 SetDParam(1, error_message);
00940 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00941 }
00942
00943 cur_company.Restore();
00944 }
00945
00950 static void DoDrawVehicle(const Vehicle *v)
00951 {
00952 SpriteID image = v->cur_image;
00953 PaletteID pal = PAL_NONE;
00954
00955 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00956
00957
00958 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
00959
00960 if (v->type == VEH_EFFECT) {
00961
00962
00963 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00964 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00965 }
00966
00967 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00968 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
00969 }
00970
00975 void ViewportAddVehicles(DrawPixelInfo *dpi)
00976 {
00977
00978 const int l = dpi->left;
00979 const int r = dpi->left + dpi->width;
00980 const int t = dpi->top;
00981 const int b = dpi->top + dpi->height;
00982
00983
00984 int xl, xu, yl, yu;
00985
00986 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
00987 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
00988 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
00989 } else {
00990
00991 xl = 0;
00992 xu = 0x3F;
00993 }
00994
00995 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
00996 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
00997 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
00998 } else {
00999
01000 yl = 0;
01001 yu = 0x3F << 6;
01002 }
01003
01004 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
01005 for (int x = xl;; x = (x + 1) & 0x3F) {
01006 const Vehicle *v = _vehicle_viewport_hash[x + y];
01007
01008 while (v != NULL) {
01009 if (!(v->vehstatus & VS_HIDDEN) &&
01010 l <= v->coord.right &&
01011 t <= v->coord.bottom &&
01012 r >= v->coord.left &&
01013 b >= v->coord.top) {
01014 DoDrawVehicle(v);
01015 }
01016 v = v->hash_viewport_next;
01017 }
01018
01019 if (x == xu) break;
01020 }
01021
01022 if (y == yu) break;
01023 }
01024 }
01025
01033 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01034 {
01035 Vehicle *found = NULL, *v;
01036 uint dist, best_dist = UINT_MAX;
01037
01038 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01039
01040 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01041 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01042
01043 FOR_ALL_VEHICLES(v) {
01044 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
01045 x >= v->coord.left && x <= v->coord.right &&
01046 y >= v->coord.top && y <= v->coord.bottom) {
01047
01048 dist = max(
01049 abs(((v->coord.left + v->coord.right) >> 1) - x),
01050 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01051 );
01052
01053 if (dist < best_dist) {
01054 found = v;
01055 best_dist = dist;
01056 }
01057 }
01058 }
01059
01060 return found;
01061 }
01062
01067 void DecreaseVehicleValue(Vehicle *v)
01068 {
01069 v->value -= v->value >> 8;
01070 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01071 }
01072
01073 static const byte _breakdown_chance[64] = {
01074 3, 3, 3, 3, 3, 3, 3, 3,
01075 4, 4, 5, 5, 6, 6, 7, 7,
01076 8, 8, 9, 9, 10, 10, 11, 11,
01077 12, 13, 13, 13, 13, 14, 15, 16,
01078 17, 19, 21, 25, 28, 31, 34, 37,
01079 40, 44, 48, 52, 56, 60, 64, 68,
01080 72, 80, 90, 100, 110, 120, 130, 140,
01081 150, 170, 190, 210, 230, 250, 250, 250,
01082 };
01083
01084 void CheckVehicleBreakdown(Vehicle *v)
01085 {
01086 int rel, rel_old;
01087
01088
01089 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01090 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01091
01092 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01093 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01094 v->cur_speed < 5 || _game_mode == GM_MENU) {
01095 return;
01096 }
01097
01098 uint32 r = Random();
01099
01100
01101 int chance = v->breakdown_chance + 1;
01102 if (Chance16I(1, 25, r)) chance += 25;
01103 v->breakdown_chance = min(255, chance);
01104
01105
01106 rel = v->reliability;
01107 if (v->type == VEH_SHIP) rel += 0x6666;
01108
01109
01110 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01111
01112
01113 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01114 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01115 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01116 v->breakdown_chance = 0;
01117 }
01118 }
01119
01126 bool Vehicle::HandleBreakdown()
01127 {
01128
01129
01130
01131
01132
01133 switch (this->breakdown_ctr) {
01134 case 0:
01135 return false;
01136
01137 case 2:
01138 this->breakdown_ctr = 1;
01139
01140 if (this->breakdowns_since_last_service != 255) {
01141 this->breakdowns_since_last_service++;
01142 }
01143
01144 if (this->type == VEH_AIRCRAFT) {
01145
01146 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01147 } else {
01148 this->cur_speed = 0;
01149
01150 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01151 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01152 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01153 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01154 }
01155
01156 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
01157 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01158 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01159 }
01160 }
01161
01162 this->MarkDirty();
01163 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01164 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01165
01166
01167 case 1:
01168
01169 if (this->type == VEH_AIRCRAFT) return false;
01170
01171
01172 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01173 if (--this->breakdown_delay == 0) {
01174 this->breakdown_ctr = 0;
01175 this->MarkDirty();
01176 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01177 }
01178 }
01179 return true;
01180
01181 default:
01182 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01183 return false;
01184 }
01185 }
01186
01191 void AgeVehicle(Vehicle *v)
01192 {
01193 if (v->age < MAX_DAY) {
01194 v->age++;
01195 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
01196 }
01197
01198 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
01199
01200 int age = v->age - v->max_age;
01201 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01202 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01203 v->reliability_spd_dec <<= 1;
01204 }
01205
01206 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01207
01208
01209 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01210
01211
01212 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
01213
01214 StringID str;
01215 if (age == -DAYS_IN_LEAP_YEAR) {
01216 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01217 } else if (age == 0) {
01218 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01219 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01220 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01221 } else {
01222 return;
01223 }
01224
01225 SetDParam(0, v->index);
01226 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01227 }
01228
01235 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01236 {
01237 int count = 0;
01238 int max = 0;
01239 int cars = 0;
01240 int unloading = 0;
01241 bool loading = false;
01242
01243 const Vehicle *u = v;
01244
01245 const Station *st = Station::GetIfValid(v->last_station_visited);
01246 assert(colour == NULL || st != NULL);
01247
01248
01249 for (; v != NULL; v = v->Next()) {
01250 count += v->cargo.OnboardCount();
01251 max += v->cargo_cap;
01252 if (v->cargo_cap != 0 && colour != NULL) {
01253 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01254 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01255 cars++;
01256 }
01257 }
01258
01259 if (colour != NULL) {
01260 if (unloading == 0 && loading) {
01261 *colour = STR_PERCENT_UP;
01262 } else if (cars == unloading || !loading) {
01263 *colour = STR_PERCENT_DOWN;
01264 } else {
01265 *colour = STR_PERCENT_UP_DOWN;
01266 }
01267 }
01268
01269
01270 if (max == 0) return 100;
01271
01272
01273 return (count * 100) / max;
01274 }
01275
01280 void VehicleEnterDepot(Vehicle *v)
01281 {
01282
01283 assert(v == v->First());
01284
01285 switch (v->type) {
01286 case VEH_TRAIN: {
01287 Train *t = Train::From(v);
01288 SetWindowClassesDirty(WC_TRAINS_LIST);
01289
01290 SetDepotReservation(t->tile, false);
01291 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01292
01293 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01294 t->wait_counter = 0;
01295 t->force_proceed = TFP_NONE;
01296 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01297 t->ConsistChanged(true);
01298 break;
01299 }
01300
01301 case VEH_ROAD:
01302 SetWindowClassesDirty(WC_ROADVEH_LIST);
01303 break;
01304
01305 case VEH_SHIP: {
01306 SetWindowClassesDirty(WC_SHIPS_LIST);
01307 Ship *ship = Ship::From(v);
01308 ship->state = TRACK_BIT_DEPOT;
01309 ship->UpdateCache();
01310 ship->UpdateViewport(true, true);
01311 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01312 break;
01313 }
01314
01315 case VEH_AIRCRAFT:
01316 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01317 HandleAircraftEnterHangar(Aircraft::From(v));
01318 break;
01319 default: NOT_REACHED();
01320 }
01321 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01322
01323 if (v->type != VEH_TRAIN) {
01324
01325
01326 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01327 }
01328 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01329
01330 v->vehstatus |= VS_HIDDEN;
01331 v->cur_speed = 0;
01332
01333 VehicleServiceInDepot(v);
01334
01335 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01336
01337 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01338 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01339
01340 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01341 Order t = v->current_order;
01342 v->current_order.MakeDummy();
01343
01344
01345
01346 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01347 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01348 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01349
01350 return;
01351 }
01352
01353 if (t.IsRefit()) {
01354 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01355 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01356 cur_company.Restore();
01357
01358 if (cost.Failed()) {
01359 _vehicles_to_autoreplace[v] = false;
01360 if (v->owner == _local_company) {
01361
01362 SetDParam(0, v->index);
01363 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01364 }
01365 } else if (cost.GetCost() != 0) {
01366 v->profit_this_year -= cost.GetCost() << 8;
01367 if (v->owner == _local_company) {
01368 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01369 }
01370 }
01371 }
01372
01373 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01374
01375 v->DeleteUnreachedImplicitOrders();
01376 UpdateVehicleTimetable(v, true);
01377 v->IncrementImplicitOrderIndex();
01378 }
01379 if (t.GetDepotActionType() & ODATFB_HALT) {
01380
01381 _vehicles_to_autoreplace[v] = false;
01382 if (v->owner == _local_company) {
01383 SetDParam(0, v->index);
01384 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01385 }
01386 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01387 }
01388 }
01389 }
01390
01391
01397 void VehicleUpdatePosition(Vehicle *v)
01398 {
01399 UpdateVehicleTileHash(v, false);
01400 }
01401
01408 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01409 {
01410 int img = v->cur_image;
01411 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01412 const Sprite *spr = GetSprite(img, ST_NORMAL);
01413
01414 pt.x += spr->x_offs;
01415 pt.y += spr->y_offs;
01416
01417 UpdateVehicleViewportHash(v, pt.x, pt.y);
01418
01419 Rect old_coord = v->coord;
01420 v->coord.left = pt.x;
01421 v->coord.top = pt.y;
01422 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01423 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01424
01425 if (dirty) {
01426 if (old_coord.left == INVALID_COORD) {
01427 MarkSingleVehicleDirty(v);
01428 } else {
01429 MarkAllViewportsDirty(
01430 min(old_coord.left, v->coord.left),
01431 min(old_coord.top, v->coord.top),
01432 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01433 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01434 );
01435 }
01436 }
01437 }
01438
01443 void VehicleUpdatePositionAndViewport(Vehicle *v)
01444 {
01445 VehicleUpdatePosition(v);
01446 VehicleUpdateViewport(v, true);
01447 }
01448
01453 void MarkSingleVehicleDirty(const Vehicle *v)
01454 {
01455 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01456 }
01457
01463 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01464 {
01465 static const int8 _delta_coord[16] = {
01466 -1,-1,-1, 0, 1, 1, 1, 0,
01467 -1, 0, 1, 1, 1, 0,-1,-1,
01468 };
01469
01470 int x = v->x_pos + _delta_coord[v->direction];
01471 int y = v->y_pos + _delta_coord[v->direction + 8];
01472
01473 GetNewVehiclePosResult gp;
01474 gp.x = x;
01475 gp.y = y;
01476 gp.old_tile = v->tile;
01477 gp.new_tile = TileVirtXY(x, y);
01478 return gp;
01479 }
01480
01481 static const Direction _new_direction_table[] = {
01482 DIR_N, DIR_NW, DIR_W,
01483 DIR_NE, DIR_SE, DIR_SW,
01484 DIR_E, DIR_SE, DIR_S
01485 };
01486
01487 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01488 {
01489 int i = 0;
01490
01491 if (y >= v->y_pos) {
01492 if (y != v->y_pos) i += 3;
01493 i += 3;
01494 }
01495
01496 if (x >= v->x_pos) {
01497 if (x != v->x_pos) i++;
01498 i++;
01499 }
01500
01501 Direction dir = v->direction;
01502
01503 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01504 if (dirdiff == DIRDIFF_SAME) return dir;
01505 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01506 }
01507
01517 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01518 {
01519 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01520 }
01521
01529 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01530 {
01531
01532 const Vehicle *v;
01533 FOR_ALL_VEHICLES(v) {
01534 if (v->type == type && v->owner == owner) {
01535 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01536 }
01537 }
01538
01539 if (this->maxid == 0) return;
01540
01541
01542
01543
01544 this->cache = CallocT<bool>(this->maxid + 2);
01545
01546
01547 FOR_ALL_VEHICLES(v) {
01548 if (v->type == type && v->owner == owner) {
01549 this->cache[v->unitnumber] = true;
01550 }
01551 }
01552 }
01553
01555 UnitID FreeUnitIDGenerator::NextID()
01556 {
01557 if (this->maxid <= this->curid) return ++this->curid;
01558
01559 while (this->cache[++this->curid]) { }
01560
01561 return this->curid;
01562 }
01563
01569 UnitID GetFreeUnitNumber(VehicleType type)
01570 {
01571
01572 uint max_veh;
01573 switch (type) {
01574 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01575 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01576 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01577 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01578 default: NOT_REACHED();
01579 }
01580
01581 const Company *c = Company::Get(_current_company);
01582 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01583
01584 FreeUnitIDGenerator gen(type, _current_company);
01585
01586 return gen.NextID();
01587 }
01588
01589
01598 bool CanBuildVehicleInfrastructure(VehicleType type)
01599 {
01600 assert(IsCompanyBuildableVehicleType(type));
01601
01602 if (!Company::IsValidID(_local_company)) return false;
01603 if (!_settings_client.gui.disable_unsuitable_building) return true;
01604
01605 UnitID max;
01606 switch (type) {
01607 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01608 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01609 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01610 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01611 default: NOT_REACHED();
01612 }
01613
01614
01615 if (max > 0) {
01616
01617 const Engine *e;
01618 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01619 if (HasBit(e->company_avail, _local_company)) return true;
01620 }
01621 return false;
01622 }
01623
01624
01625 const Vehicle *v;
01626 FOR_ALL_VEHICLES(v) {
01627 if (v->owner == _local_company && v->type == type) return true;
01628 }
01629
01630 return false;
01631 }
01632
01633
01641 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01642 {
01643 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01644 const Engine *e = Engine::Get(engine_type);
01645 switch (e->type) {
01646 default: NOT_REACHED();
01647 case VEH_TRAIN:
01648 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01649
01650
01651 engine_type = parent_engine_type;
01652 e = Engine::Get(engine_type);
01653
01654 }
01655
01656 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01657 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01658 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01659 if (!CargoSpec::Get(cargo_type)->is_freight) {
01660 if (parent_engine_type == INVALID_ENGINE) {
01661 return LS_PASSENGER_WAGON_STEAM;
01662 } else {
01663 switch (RailVehInfo(parent_engine_type)->engclass) {
01664 default: NOT_REACHED();
01665 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01666 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01667 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01668 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01669 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01670 }
01671 }
01672 } else {
01673 return LS_FREIGHT_WAGON;
01674 }
01675 } else {
01676 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01677
01678 switch (e->u.rail.engclass) {
01679 default: NOT_REACHED();
01680 case EC_STEAM: return LS_STEAM;
01681 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01682 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01683 case EC_MONORAIL: return LS_MONORAIL;
01684 case EC_MAGLEV: return LS_MAGLEV;
01685 }
01686 }
01687
01688 case VEH_ROAD:
01689
01690 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01691 engine_type = parent_engine_type;
01692 e = Engine::Get(engine_type);
01693 cargo_type = v->First()->cargo_type;
01694 }
01695 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01696 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01697
01698
01699 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01700
01701 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01702 } else {
01703
01704 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01705 }
01706
01707 case VEH_SHIP:
01708 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01709 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01710 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01711
01712 case VEH_AIRCRAFT:
01713 switch (e->u.air.subtype) {
01714 case AIR_HELI: return LS_HELICOPTER;
01715 case AIR_CTOL: return LS_SMALL_PLANE;
01716 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01717 default: NOT_REACHED();
01718 }
01719 }
01720 }
01721
01731 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01732 {
01733 const Company *c = Company::Get(company);
01734 LiveryScheme scheme = LS_DEFAULT;
01735
01736
01737
01738 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01739
01740 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01741
01742
01743 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01744 }
01745
01746 return &c->livery[scheme];
01747 }
01748
01749
01750 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01751 {
01752 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01753
01754
01755 if (map != PAL_NONE) return map;
01756
01757 const Engine *e = Engine::Get(engine_type);
01758
01759
01760 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01761 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01762
01763 if (callback != CALLBACK_FAILED) {
01764 assert_compile(PAL_NONE == 0);
01765 map = GB(callback, 0, 14);
01766
01767
01768 if (!HasBit(callback, 14)) {
01769
01770 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01771 return map;
01772 }
01773 }
01774 }
01775
01776 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01777
01778 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01779
01780
01781 if (!Company::IsValidID(company)) return map;
01782
01783 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01784
01785 map += livery->colour1;
01786 if (twocc) map += livery->colour2 * 16;
01787
01788
01789 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01790 return map;
01791 }
01792
01799 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01800 {
01801 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01802 }
01803
01809 PaletteID GetVehiclePalette(const Vehicle *v)
01810 {
01811 if (v->IsGroundVehicle()) {
01812 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01813 }
01814
01815 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01816 }
01817
01821 void Vehicle::DeleteUnreachedImplicitOrders()
01822 {
01823 if (this->IsGroundVehicle()) {
01824 uint16 &gv_flags = this->GetGroundVehicleFlags();
01825 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01826
01827 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01828 this->cur_implicit_order_index = this->cur_real_order_index;
01829 InvalidateVehicleOrder(this, 0);
01830 return;
01831 }
01832 }
01833
01834 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01835 while (order != NULL) {
01836 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01837
01838 if (order->IsType(OT_IMPLICIT)) {
01839
01840 order = order->next;
01841 DeleteOrder(this, this->cur_implicit_order_index);
01842 } else {
01843
01844 order = order->next;
01845 this->cur_implicit_order_index++;
01846 }
01847
01848
01849 if (order == NULL) {
01850 order = this->GetOrder(0);
01851 this->cur_implicit_order_index = 0;
01852 }
01853 }
01854 }
01855
01860 void Vehicle::BeginLoading()
01861 {
01862 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01863
01864 if (this->current_order.IsType(OT_GOTO_STATION) &&
01865 this->current_order.GetDestination() == this->last_station_visited) {
01866 this->DeleteUnreachedImplicitOrders();
01867
01868
01869 this->current_order.MakeLoading(true);
01870 UpdateVehicleTimetable(this, true);
01871
01872
01873
01874
01875
01876
01877 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01878
01879 } else {
01880
01881
01882
01883
01884
01885 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01886 if (this->IsGroundVehicle() && in_list != NULL &&
01887 (!in_list->IsType(OT_IMPLICIT) ||
01888 in_list->GetDestination() != this->last_station_visited)) {
01889 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01890
01891 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01892 if (prev_order == NULL ||
01893 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01894 prev_order->GetDestination() != this->last_station_visited) {
01895
01896
01897
01898 int target_index = this->cur_implicit_order_index;
01899 bool found = false;
01900 while (target_index != this->cur_real_order_index) {
01901 const Order *order = this->GetOrder(target_index);
01902 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01903 found = true;
01904 break;
01905 }
01906 target_index++;
01907 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01908 assert(target_index != this->cur_implicit_order_index);
01909 }
01910
01911 if (found) {
01912 if (suppress_implicit_orders) {
01913
01914 this->cur_implicit_order_index = target_index;
01915 InvalidateVehicleOrder(this, 0);
01916 } else {
01917
01918 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01919 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01920 if (order->IsType(OT_IMPLICIT)) {
01921
01922 order = order->next;
01923 DeleteOrder(this, this->cur_implicit_order_index);
01924 } else {
01925
01926 order = order->next;
01927 this->cur_implicit_order_index++;
01928 }
01929
01930
01931 if (order == NULL) {
01932 order = this->GetOrder(0);
01933 this->cur_implicit_order_index = 0;
01934 }
01935 assert(order != NULL);
01936 }
01937 }
01938 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01939
01940 Order *implicit_order = new Order();
01941 implicit_order->MakeImplicit(this->last_station_visited);
01942 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01943 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01944
01945
01946
01947 uint16 &gv_flags = this->GetGroundVehicleFlags();
01948 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01949 }
01950 }
01951 }
01952 this->current_order.MakeLoading(false);
01953 }
01954
01955 if (this->last_loading_station != INVALID_STATION &&
01956 this->last_loading_station != this->last_station_visited &&
01957 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
01958 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
01959 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
01960 }
01961
01962 PrepareUnload(this);
01963
01964 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01965 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
01966 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01967 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01968
01969 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01970 this->cur_speed = 0;
01971 this->MarkDirty();
01972 }
01973
01978 void Vehicle::CancelReservation(StationID next, Station *st)
01979 {
01980 for (Vehicle *v = this; v != NULL; v = v->next) {
01981 VehicleCargoList &cargo = v->cargo;
01982 if (cargo.ReservedCount() > 0) {
01983 DEBUG(misc, 1, "cancelling cargo reservation");
01984 GoodsEntry &ge = st->goods[v->cargo_type];
01985 cargo.Unreserve(next, &ge.cargo);
01986 SetBit(ge.acceptance_pickup, GoodsEntry::GES_PICKUP);
01987 }
01988 }
01989 }
01990
01995 void Vehicle::LeaveStation()
01996 {
01997 assert(this->current_order.IsType(OT_LOADING));
01998
01999 delete this->cargo_payment;
02000
02001
02002 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
02003
02004 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
02005 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
02006 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
02007
02008
02009
02010
02011 this->RefreshNextHopsStats();
02012
02013
02014 this->last_loading_station = this->last_station_visited;
02015 } else {
02016
02017
02018
02019 this->last_loading_station = INVALID_STATION;
02020 }
02021 }
02022
02023 this->current_order.MakeLeaveStation();
02024 Station *st = Station::Get(this->last_station_visited);
02025 this->CancelReservation(INVALID_STATION, st);
02026 st->loading_vehicles.remove(this);
02027
02028 HideFillingPercent(&this->fill_percent_te_id);
02029
02030 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02031
02032 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02033
02034 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02035 }
02036 }
02037
02044 void Vehicle::RefreshNextHopsStats()
02045 {
02046
02047 SmallMap<CargoID, uint, 1> capacities;
02048 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02049 v->refit_cap = v->cargo_cap;
02050 if (v->refit_cap == 0) continue;
02051 SmallPair<CargoID, uint> *i = capacities.Find(v->cargo_type);
02052 if (i == capacities.End()) {
02053
02054 i = capacities.Append();
02055 i->first = v->cargo_type;
02056 i->second = v->cargo_cap;
02057 } else {
02058 i->second += v->cargo_cap;
02059 }
02060 }
02061
02062
02063 if (this->orders.list == NULL) return;
02064
02065 uint hops = 0;
02066 const Order *first = this->orders.list->GetNextStoppingOrder(this,
02067 this->GetOrder(this->cur_implicit_order_index), hops);
02068 const Order *cur = first;
02069 const Order *next = first;
02070 while (next != NULL && cur->CanLeaveWithCargo(true)) {
02071 next = this->orders.list->GetNextStoppingOrder(this,
02072 this->orders.list->GetNext(next), ++hops);
02073 if (next == NULL) break;
02074
02075 if (next->IsType(OT_GOTO_DEPOT)) {
02076
02077 CargoID new_cid = next->GetRefitCargo();
02078 byte new_subtype = next->GetRefitSubtype();
02079 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02080 const Engine *e = Engine::Get(v->engine_type);
02081 if (!HasBit(e->info.refit_mask, new_cid)) continue;
02082
02083
02084 CargoID temp_cid = v->cargo_type;
02085 byte temp_subtype = v->cargo_subtype;
02086 v->cargo_type = new_cid;
02087 v->cargo_subtype = new_subtype;
02088
02089 uint16 mail_capacity = 0;
02090 uint amount = e->DetermineCapacity(v, &mail_capacity);
02091
02092
02093 v->cargo_type = temp_cid;
02094 v->cargo_subtype = temp_subtype;
02095
02096
02097 if (new_cid != v->cargo_type && v->refit_cap > 0) {
02098 capacities[v->cargo_type] -= v->refit_cap;
02099 v->refit_cap = 0;
02100 } else if (amount < v->refit_cap) {
02101 capacities[v->cargo_type] -= v->refit_cap - amount;
02102 v->refit_cap = amount;
02103 }
02104
02105
02106 if (v->type == VEH_AIRCRAFT) {
02107 Vehicle *u = v->Next();
02108 if (mail_capacity < u->refit_cap) {
02109 capacities[u->cargo_type] -= u->refit_cap - mail_capacity;
02110 u->refit_cap = mail_capacity;
02111 }
02112 break;
02113 }
02114 if (v->type == VEH_SHIP) break;
02115 }
02116 } else {
02117 StationID next_station = next->GetDestination();
02118 Station *st = Station::GetIfValid(cur->GetDestination());
02119 if (st != NULL && next_station != INVALID_STATION && next_station != st->index) {
02120 for (const SmallPair<CargoID, uint> *i = capacities.Begin(); i != capacities.End(); ++i) {
02121
02122 if (i->second > 0) IncreaseStats(st, i->first, next_station, i->second, UINT_MAX);
02123 }
02124 }
02125 cur = next;
02126 if (cur == first) break;
02127 }
02128 }
02129
02130 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
02131 }
02132
02138 void Vehicle::HandleLoading(bool mode)
02139 {
02140 switch (this->current_order.GetType()) {
02141 case OT_LOADING: {
02142 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02143
02144
02145 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02146
02147 this->PlayLeaveStationSound();
02148
02149 this->LeaveStation();
02150
02151
02152 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02153 if (order == NULL ||
02154 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02155 order->GetDestination() != this->last_station_visited) {
02156 return;
02157 }
02158 break;
02159 }
02160
02161 case OT_DUMMY: break;
02162
02163 default: return;
02164 }
02165
02166 this->IncrementImplicitOrderIndex();
02167 }
02168
02173 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
02174 {
02175 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02176 if (v->cargo_cap == 0) continue;
02177 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
02178 if (pair == capacities.End()) {
02179 pair = capacities.Append();
02180 pair->first = v->cargo_type;
02181 pair->second = v->cargo_cap - v->cargo.Count();
02182 } else {
02183 pair->second += v->cargo_cap - v->cargo.Count();
02184 }
02185 }
02186 }
02187
02188 uint Vehicle::GetConsistTotalCapacity() const
02189 {
02190 uint result = 0;
02191 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02192 result += v->cargo_cap;
02193 }
02194 return result;
02195 }
02196
02203 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02204 {
02205 CommandCost ret = CheckOwnership(this->owner);
02206 if (ret.Failed()) return ret;
02207
02208 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02209 if (this->IsStoppedInDepot()) return CMD_ERROR;
02210
02211 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02212 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02213 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02214
02215
02216
02217 if (flags & DC_EXEC) {
02218 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02219 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02220 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02221 }
02222 return CommandCost();
02223 }
02224
02225 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02226 if (flags & DC_EXEC) {
02227
02228
02229 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02230
02231 if (this->IsGroundVehicle()) {
02232 uint16 &gv_flags = this->GetGroundVehicleFlags();
02233 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02234 }
02235
02236 this->current_order.MakeDummy();
02237 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02238 }
02239 return CommandCost();
02240 }
02241
02242 TileIndex location;
02243 DestinationID destination;
02244 bool reverse;
02245 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};
02246 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02247
02248 if (flags & DC_EXEC) {
02249 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02250
02251 if (this->IsGroundVehicle()) {
02252 uint16 &gv_flags = this->GetGroundVehicleFlags();
02253 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02254 }
02255
02256 this->dest_tile = location;
02257 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02258 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02259 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02260
02261
02262 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02263
02264 if (this->type == VEH_AIRCRAFT) {
02265 Aircraft *a = Aircraft::From(this);
02266 if (a->state == FLYING && a->targetairport != destination) {
02267
02268 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02269 AircraftNextAirportPos_and_Order(a);
02270 }
02271 }
02272 }
02273
02274 return CommandCost();
02275
02276 }
02277
02282 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02283 {
02284 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02285 const Engine *e = this->GetEngine();
02286
02287
02288 byte visual_effect;
02289 switch (e->type) {
02290 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02291 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02292 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02293 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02294 }
02295
02296
02297 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02298 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02299
02300 if (callback != CALLBACK_FAILED) {
02301 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02302
02303 callback = GB(callback, 0, 8);
02304
02305
02306 if (callback == VE_DEFAULT) {
02307 assert(HasBit(callback, VE_DISABLE_EFFECT));
02308 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02309 }
02310 visual_effect = callback;
02311 }
02312 }
02313
02314
02315 if (visual_effect == VE_DEFAULT ||
02316 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02317
02318
02319 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02320 if (visual_effect == VE_DEFAULT) {
02321 visual_effect = 1 << VE_DISABLE_EFFECT;
02322 } else {
02323 SetBit(visual_effect, VE_DISABLE_EFFECT);
02324 }
02325 } else {
02326 if (visual_effect == VE_DEFAULT) {
02327
02328 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02329 }
02330 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02331 }
02332 }
02333
02334 this->vcache.cached_vis_effect = visual_effect;
02335
02336 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02337 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02338 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02339 }
02340 }
02341
02342 static const int8 _vehicle_smoke_pos[8] = {
02343 1, 1, 1, 0, -1, -1, -1, 0
02344 };
02345
02350 void Vehicle::ShowVisualEffect() const
02351 {
02352 assert(this->IsPrimaryVehicle());
02353 bool sound = false;
02354
02355
02356
02357
02358
02359
02360 if (_settings_game.vehicle.smoke_amount == 0 ||
02361 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02362 this->cur_speed < 2) {
02363 return;
02364 }
02365
02366 uint max_speed = this->vcache.cached_max_speed;
02367 if (this->type == VEH_TRAIN) {
02368 const Train *t = Train::From(this);
02369
02370
02371
02372
02373 if (HasBit(t->flags, VRF_REVERSING) ||
02374 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02375 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02376 return;
02377 }
02378
02379 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02380 max_speed = min(max_speed, this->current_order.max_speed);
02381 }
02382 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02383
02384 const Vehicle *v = this;
02385
02386 do {
02387 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02388 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02389 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02390
02391
02392
02393
02394
02395
02396
02397
02398 if (disable_effect ||
02399 v->vehstatus & VS_HIDDEN ||
02400 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02401 IsDepotTile(v->tile) ||
02402 IsTunnelTile(v->tile) ||
02403 (v->type == VEH_TRAIN &&
02404 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02405 continue;
02406 }
02407
02408
02409
02410
02411 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02412
02413 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02414 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02415
02416 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02417 x = -x;
02418 y = -y;
02419 }
02420
02421 switch (effect_type) {
02422 case VE_TYPE_STEAM:
02423
02424
02425
02426
02427
02428 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02429 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02430 sound = true;
02431 }
02432 break;
02433
02434 case VE_TYPE_DIESEL: {
02435
02436
02437
02438
02439
02440
02441
02442
02443
02444
02445
02446 int power_weight_effect = 0;
02447 if (v->type == VEH_TRAIN) {
02448 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02449 }
02450 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02451 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02452 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02453 sound = true;
02454 }
02455 break;
02456 }
02457
02458 case VE_TYPE_ELECTRIC:
02459
02460
02461
02462
02463
02464
02465 if (GB(v->tick_counter, 0, 2) == 0 &&
02466 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02467 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02468 sound = true;
02469 }
02470 break;
02471
02472 default:
02473 break;
02474 }
02475 } while ((v = v->Next()) != NULL);
02476
02477 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02478 }
02479
02484 void Vehicle::SetNext(Vehicle *next)
02485 {
02486 assert(this != next);
02487
02488 if (this->next != NULL) {
02489
02490 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02491 v->first = this->next;
02492 }
02493 this->next->previous = NULL;
02494 }
02495
02496 this->next = next;
02497
02498 if (this->next != NULL) {
02499
02500 if (this->next->previous != NULL) this->next->previous->next = NULL;
02501 this->next->previous = this;
02502 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02503 v->first = this->first;
02504 }
02505 }
02506 }
02507
02513 void Vehicle::AddToShared(Vehicle *shared_chain)
02514 {
02515 assert(this->previous_shared == NULL && this->next_shared == NULL);
02516
02517 if (shared_chain->orders.list == NULL) {
02518 assert(shared_chain->previous_shared == NULL);
02519 assert(shared_chain->next_shared == NULL);
02520 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02521 }
02522
02523 this->next_shared = shared_chain->next_shared;
02524 this->previous_shared = shared_chain;
02525
02526 shared_chain->next_shared = this;
02527
02528 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02529
02530 shared_chain->orders.list->AddVehicle(this);
02531 }
02532
02536 void Vehicle::RemoveFromShared()
02537 {
02538
02539
02540 bool were_first = (this->FirstShared() == this);
02541 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02542
02543 this->orders.list->RemoveVehicle(this);
02544
02545 if (!were_first) {
02546
02547 this->previous_shared->next_shared = this->NextShared();
02548 }
02549
02550 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02551
02552
02553 if (this->orders.list->GetNumVehicles() == 1) {
02554
02555 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02556 InvalidateVehicleOrder(this->FirstShared(), 0);
02557 } else if (were_first) {
02558
02559
02560 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02561 }
02562
02563 this->next_shared = NULL;
02564 this->previous_shared = NULL;
02565 }
02566
02567 void VehiclesYearlyLoop()
02568 {
02569 Vehicle *v;
02570 FOR_ALL_VEHICLES(v) {
02571 if (v->IsPrimaryVehicle()) {
02572
02573 Money profit = v->GetDisplayProfitThisYear();
02574 if (v->age >= 730 && profit < 0) {
02575 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02576 SetDParam(0, v->index);
02577 SetDParam(1, profit);
02578 AddVehicleNewsItem(
02579 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02580 NS_ADVICE,
02581 v->index
02582 );
02583 }
02584 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02585 }
02586
02587 v->profit_last_year = v->profit_this_year;
02588 v->profit_this_year = 0;
02589 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02590 }
02591 }
02592 GroupStatistics::UpdateProfits();
02593 SetWindowClassesDirty(WC_TRAINS_LIST);
02594 SetWindowClassesDirty(WC_SHIPS_LIST);
02595 SetWindowClassesDirty(WC_ROADVEH_LIST);
02596 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02597 }
02598
02599
02609 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02610 {
02611 const Engine *e = Engine::GetIfValid(engine_type);
02612 assert(e != NULL);
02613
02614 switch (e->type) {
02615 case VEH_TRAIN:
02616 return (st->facilities & FACIL_TRAIN) != 0;
02617
02618 case VEH_ROAD:
02619
02620
02621
02622 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02623
02624 case VEH_SHIP:
02625 return (st->facilities & FACIL_DOCK) != 0;
02626
02627 case VEH_AIRCRAFT:
02628 return (st->facilities & FACIL_AIRPORT) != 0 &&
02629 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02630
02631 default:
02632 return false;
02633 }
02634 }
02635
02642 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02643 {
02644 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02645
02646 return CanVehicleUseStation(v->engine_type, st);
02647 }
02648
02654 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02655 {
02656 assert(this->IsGroundVehicle());
02657 if (this->type == VEH_TRAIN) {
02658 return &Train::From(this)->gcache;
02659 } else {
02660 return &RoadVehicle::From(this)->gcache;
02661 }
02662 }
02663
02669 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02670 {
02671 assert(this->IsGroundVehicle());
02672 if (this->type == VEH_TRAIN) {
02673 return &Train::From(this)->gcache;
02674 } else {
02675 return &RoadVehicle::From(this)->gcache;
02676 }
02677 }
02678
02684 uint16 &Vehicle::GetGroundVehicleFlags()
02685 {
02686 assert(this->IsGroundVehicle());
02687 if (this->type == VEH_TRAIN) {
02688 return Train::From(this)->gv_flags;
02689 } else {
02690 return RoadVehicle::From(this)->gv_flags;
02691 }
02692 }
02693
02699 const uint16 &Vehicle::GetGroundVehicleFlags() const
02700 {
02701 assert(this->IsGroundVehicle());
02702 if (this->type == VEH_TRAIN) {
02703 return Train::From(this)->gv_flags;
02704 } else {
02705 return RoadVehicle::From(this)->gv_flags;
02706 }
02707 }
02708
02717 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02718 {
02719 if (v->type == VEH_TRAIN) {
02720 Train *u = Train::From(v);
02721
02722 u = u->GetFirstEnginePart();
02723
02724
02725 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02726 do {
02727
02728 set.Include(u->index);
02729
02730
02731 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02732
02733 u = u->Next();
02734 } while (u != NULL && u->IsArticulatedPart());
02735 }
02736 }
02737 }