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