00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "gui.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 "vehicle_gui.h"
00023 #include "train.h"
00024 #include "aircraft.h"
00025 #include "newgrf_debug.h"
00026 #include "newgrf_sound.h"
00027 #include "newgrf_station.h"
00028 #include "group.h"
00029 #include "group_gui.h"
00030 #include "strings_func.h"
00031 #include "zoom_func.h"
00032 #include "date_func.h"
00033 #include "window_func.h"
00034 #include "vehicle_func.h"
00035 #include "autoreplace_func.h"
00036 #include "autoreplace_gui.h"
00037 #include "station_base.h"
00038 #include "ai/ai.hpp"
00039 #include "depot_func.h"
00040 #include "network/network.h"
00041 #include "core/pool_func.hpp"
00042 #include "economy_base.h"
00043 #include "articulated_vehicles.h"
00044 #include "roadstop_base.h"
00045 #include "core/random_func.hpp"
00046 #include "core/backup_type.hpp"
00047 #include "order_backup.h"
00048 #include "sound_func.h"
00049 #include "effectvehicle_func.h"
00050 #include "effectvehicle_base.h"
00051 #include "vehiclelist.h"
00052 #include "bridge_map.h"
00053 #include "tunnel_map.h"
00054 #include "depot_map.h"
00055 #include "cargodest_func.h"
00056
00057 #include "table/strings.h"
00058
00059 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00060
00061 VehicleID _new_vehicle_id;
00062 uint16 _returned_refit_capacity;
00063 uint16 _returned_mail_refit_capacity;
00064 byte _age_cargo_skip_counter;
00065
00066
00068 VehiclePool _vehicle_pool("Vehicle");
00069 INSTANTIATE_POOL_METHODS(Vehicle)
00070
00071
00076 bool Vehicle::NeedsAutorenewing(const Company *c) const
00077 {
00078
00079
00080
00081
00082 assert(c == Company::Get(this->owner));
00083
00084 if (!c->settings.engine_renew) return false;
00085 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00086 if (this->age == 0) return false;
00087
00088 return true;
00089 }
00090
00091 void VehicleServiceInDepot(Vehicle *v)
00092 {
00093 v->date_of_last_service = _date;
00094 v->breakdowns_since_last_service = 0;
00095 v->reliability = Engine::Get(v->engine_type)->reliability;
00096 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00097 }
00098
00105 bool Vehicle::NeedsServicing() const
00106 {
00107
00108
00109 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00110
00111
00112 const Company *c = Company::Get(this->owner);
00113 if (c->settings.vehicle.servint_ispercent ?
00114 (this->reliability >= Engine::Get(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00115 (this->date_of_last_service + this->service_interval >= _date)) {
00116 return false;
00117 }
00118
00119
00120
00121 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00122 _settings_game.difficulty.vehicle_breakdowns != 0) {
00123 return true;
00124 }
00125
00126
00127
00128
00129 bool pending_replace = false;
00130 Money needed_money = c->settings.engine_renew_money;
00131 if (needed_money > c->money) return false;
00132
00133 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00134 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
00135
00136
00137 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00138
00139
00140 uint32 available_cargo_types, union_mask;
00141 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00142
00143 if (union_mask != 0) {
00144 CargoID cargo_type;
00145
00146 if (IsArticulatedVehicleCarryingDifferentCargos(v, &cargo_type)) continue;
00147
00148
00149 if (cargo_type != CT_INVALID) {
00150
00151 if (!HasBit(available_cargo_types, cargo_type)) continue;
00152 }
00153 }
00154
00155
00156
00157 pending_replace = true;
00158 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00159 if (needed_money > c->money) return false;
00160 }
00161
00162 return pending_replace;
00163 }
00164
00170 bool Vehicle::NeedsAutomaticServicing() const
00171 {
00172 if (this->HasDepotOrder()) return false;
00173 if (this->current_order.IsType(OT_LOADING)) return false;
00174 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00175 return NeedsServicing();
00176 }
00177
00178 uint Vehicle::Crash(bool flooded)
00179 {
00180 assert((this->vehstatus & VS_CRASHED) == 0);
00181 assert(this->Previous() == NULL);
00182
00183 uint pass = 0;
00184
00185 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00186
00187 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00188 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00189 v->vehstatus |= VS_CRASHED;
00190 MarkSingleVehicleDirty(v);
00191 }
00192
00193
00194 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00195 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
00196 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00197 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00198
00199 return pass;
00200 }
00201
00202
00211 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00212 {
00213 const Engine *e = Engine::Get(engine);
00214 uint32 grfid = e->grf_prop.grffile->grfid;
00215 GRFConfig *grfconfig = GetGRFConfig(grfid);
00216
00217 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00218 SetBit(grfconfig->grf_bugs, bug_type);
00219 SetDParamStr(0, grfconfig->GetName());
00220 SetDParam(1, engine);
00221 ShowErrorMessage(part1, part2, WL_CRITICAL);
00222 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00223 }
00224
00225
00226 char buffer[512];
00227
00228 SetDParamStr(0, grfconfig->GetName());
00229 GetString(buffer, part1, lastof(buffer));
00230 DEBUG(grf, 0, "%s", buffer + 3);
00231
00232 SetDParam(1, engine);
00233 GetString(buffer, part2, lastof(buffer));
00234 DEBUG(grf, 0, "%s", buffer + 3);
00235 }
00236
00241 Vehicle::Vehicle(VehicleType type)
00242 {
00243 this->type = type;
00244 this->coord.left = INVALID_COORD;
00245 this->group_id = DEFAULT_GROUP;
00246 this->fill_percent_te_id = INVALID_TE_ID;
00247 this->first = this;
00248 this->colourmap = PAL_NONE;
00249 this->last_station_loaded = INVALID_STATION;
00250 this->current_order.index = INVALID_ORDER;
00251 this->last_order_id = INVALID_ORDER;
00252 }
00253
00258 byte VehicleRandomBits()
00259 {
00260 return GB(Random(), 0, 8);
00261 }
00262
00263
00264
00265 const int HASH_BITS = 7;
00266 const int HASH_SIZE = 1 << HASH_BITS;
00267 const int HASH_MASK = HASH_SIZE - 1;
00268 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00269 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00270
00271
00272
00273 const int HASH_RES = 0;
00274
00275 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00276
00277 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00278 {
00279 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00280 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00281 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00282 for (; v != NULL; v = v->next_new_hash) {
00283 Vehicle *a = proc(v, data);
00284 if (find_first && a != NULL) return a;
00285 }
00286 if (x == xu) break;
00287 }
00288 if (y == yu) break;
00289 }
00290
00291 return NULL;
00292 }
00293
00294
00306 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00307 {
00308 const int COLL_DIST = 6;
00309
00310
00311 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00312 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00313 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00314 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00315
00316 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00317 }
00318
00333 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00334 {
00335 VehicleFromPosXY(x, y, data, proc, false);
00336 }
00337
00349 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00350 {
00351 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00352 }
00353
00364 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00365 {
00366 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00367 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00368
00369 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00370 for (; v != NULL; v = v->next_new_hash) {
00371 if (v->tile != tile) continue;
00372
00373 Vehicle *a = proc(v, data);
00374 if (find_first && a != NULL) return a;
00375 }
00376
00377 return NULL;
00378 }
00379
00393 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00394 {
00395 VehicleFromPos(tile, data, proc, false);
00396 }
00397
00408 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00409 {
00410 return VehicleFromPos(tile, data, proc, true) != NULL;
00411 }
00412
00419 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00420 {
00421 byte z = *(byte*)data;
00422
00423 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00424 if (v->z_pos > z) return NULL;
00425
00426 return v;
00427 }
00428
00434 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00435 {
00436 byte z = GetTileMaxZ(tile);
00437
00438
00439
00440
00441
00442 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00443 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00444 return CommandCost();
00445 }
00446
00448 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00449 {
00450 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00451 if (v == (const Vehicle *)data) return NULL;
00452
00453 return v;
00454 }
00455
00463 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00464 {
00465
00466
00467
00468
00469 Vehicle *v = VehicleFromPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00470 if (v == NULL) v = VehicleFromPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00471
00472 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00473 return CommandCost();
00474 }
00475
00476 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00477 {
00478 TrackBits rail_bits = *(TrackBits *)data;
00479
00480 if (v->type != VEH_TRAIN) return NULL;
00481
00482 Train *t = Train::From(v);
00483 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00484
00485 return v;
00486 }
00487
00496 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00497 {
00498
00499
00500
00501
00502 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00503 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00504 return CommandCost();
00505 }
00506
00507 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00508 {
00509 Vehicle **old_hash = v->old_new_hash;
00510 Vehicle **new_hash;
00511
00512 if (remove) {
00513 new_hash = NULL;
00514 } else {
00515 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00516 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00517 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00518 }
00519
00520 if (old_hash == new_hash) return;
00521
00522
00523 if (old_hash != NULL) {
00524 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = v->prev_new_hash;
00525 *v->prev_new_hash = v->next_new_hash;
00526 }
00527
00528
00529 if (new_hash != NULL) {
00530 v->next_new_hash = *new_hash;
00531 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = &v->next_new_hash;
00532 v->prev_new_hash = new_hash;
00533 *new_hash = v;
00534 }
00535
00536
00537 v->old_new_hash = new_hash;
00538 }
00539
00540 static Vehicle *_vehicle_position_hash[0x1000];
00541
00542 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00543 {
00544 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00545
00546 Vehicle **old_hash, **new_hash;
00547 int old_x = v->coord.left;
00548 int old_y = v->coord.top;
00549
00550 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00551 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00552
00553 if (old_hash == new_hash) return;
00554
00555
00556 if (old_hash != NULL) {
00557 if (v->next_hash != NULL) v->next_hash->prev_hash = v->prev_hash;
00558 *v->prev_hash = v->next_hash;
00559 }
00560
00561
00562 if (new_hash != NULL) {
00563 v->next_hash = *new_hash;
00564 if (v->next_hash != NULL) v->next_hash->prev_hash = &v->next_hash;
00565 v->prev_hash = new_hash;
00566 *new_hash = v;
00567 }
00568 }
00569
00570 void ResetVehiclePosHash()
00571 {
00572 Vehicle *v;
00573 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00574 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00575 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00576 }
00577
00578 void ResetVehicleColourMap()
00579 {
00580 Vehicle *v;
00581 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00582 }
00583
00588 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00589 static AutoreplaceMap _vehicles_to_autoreplace;
00590
00591 void InitializeVehicles()
00592 {
00593 _age_cargo_skip_counter = 1;
00594
00595 _vehicles_to_autoreplace.Reset();
00596 ResetVehiclePosHash();
00597 }
00598
00599 uint CountVehiclesInChain(const Vehicle *v)
00600 {
00601 uint count = 0;
00602 do count++; while ((v = v->Next()) != NULL);
00603 return count;
00604 }
00605
00611 void CountCompanyVehicles(CompanyID cid, uint counts[4])
00612 {
00613 for (uint i = 0; i < 4; i++) counts[i] = 0;
00614
00615 const Vehicle *v;
00616 FOR_ALL_VEHICLES(v) {
00617 if (v->owner == cid && v->IsPrimaryVehicle()) counts[v->type]++;
00618 }
00619 }
00620
00625 bool Vehicle::IsEngineCountable() const
00626 {
00627 switch (this->type) {
00628 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00629 case VEH_TRAIN:
00630 return !this->IsArticulatedPart() &&
00631 !Train::From(this)->IsRearDualheaded();
00632 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00633 case VEH_SHIP: return true;
00634 default: return false;
00635 }
00636 }
00637
00645 void Vehicle::HandlePathfindingResult(bool path_found)
00646 {
00647 if (path_found) {
00648
00649 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00650
00651
00652 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00653
00654 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00655 return;
00656 }
00657
00658
00659 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00660
00661
00662 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00663
00664 AI::NewEvent(this->owner, new AIEventVehicleLost(this->index));
00665 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00666 SetDParam(0, this->index);
00667 AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00668 }
00669 }
00670
00672 void Vehicle::PreDestructor()
00673 {
00674 if (CleaningPool()) return;
00675
00676 if (Station::IsValidID(this->last_station_visited)) {
00677 Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00678
00679 HideFillingPercent(&this->fill_percent_te_id);
00680
00681 delete this->cargo_payment;
00682 }
00683
00684 if (this->IsEngineCountable()) {
00685 Company::Get(this->owner)->num_engines[this->engine_type]--;
00686 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00687
00688 DeleteGroupHighlightOfVehicle(this);
00689 if (Group::IsValidID(this->group_id)) Group::Get(this->group_id)->num_engines[this->engine_type]--;
00690 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00691 }
00692
00693 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00694 Aircraft *a = Aircraft::From(this);
00695 Station *st = GetTargetAirportIfValid(a);
00696 if (st != NULL) {
00697 const AirportFTA *layout = st->airport.GetFTA()->layout;
00698 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00699 }
00700 }
00701
00702
00703 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00704 RoadVehicle *v = RoadVehicle::From(this);
00705 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00706
00707 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00708 }
00709 }
00710
00711 if (this->Previous() == NULL) {
00712 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00713 }
00714
00715 if (this->IsPrimaryVehicle()) {
00716 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00717 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00718 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00719 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00720 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00721 SetWindowDirty(WC_COMPANY, this->owner);
00722 OrderBackup::ClearVehicle(this);
00723 }
00724 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00725
00726 this->cargo.Truncate(0);
00727 DeleteVehicleOrders(this);
00728 DeleteDepotHighlightOfVehicle(this);
00729
00730 extern void StopGlobalFollowVehicle(const Vehicle *v);
00731 StopGlobalFollowVehicle(this);
00732
00733 ReleaseDisastersTargetingVehicle(this->index);
00734 }
00735
00736 Vehicle::~Vehicle()
00737 {
00738 free(this->name);
00739
00740 if (CleaningPool()) {
00741 this->cargo.OnCleanPool();
00742 return;
00743 }
00744
00745
00746
00747 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00748
00749 Vehicle *v = this->Next();
00750 this->SetNext(NULL);
00751
00752 delete v;
00753
00754 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00755 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00756 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00757 }
00758
00763 void VehicleEnteredDepotThisTick(Vehicle *v)
00764 {
00765
00766 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00767
00768
00769
00770
00771
00772
00773 v->vehstatus |= VS_STOPPED;
00774 }
00775
00781 static void RunVehicleDayProc()
00782 {
00783 if (_game_mode != GM_NORMAL) return;
00784
00785
00786 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00787 Vehicle *v = Vehicle::Get(i);
00788 if (v == NULL) continue;
00789
00790
00791 if ((v->day_counter & 0x1F) == 0) {
00792 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00793 if (callback != CALLBACK_FAILED) {
00794 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00795 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00796 }
00797 }
00798
00799
00800 v->OnNewDay();
00801 }
00802 }
00803
00804 void CallVehicleTicks()
00805 {
00806 _vehicles_to_autoreplace.Clear();
00807
00808 _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? CARGO_AGING_TICKS - 1 : (_age_cargo_skip_counter - 1);
00809
00810 RunVehicleDayProc();
00811
00812 Station *st;
00813 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00814
00815 Vehicle *v;
00816 FOR_ALL_VEHICLES(v) {
00817
00818 if (!v->Tick()) {
00819 assert(Vehicle::Get(vehicle_index) == NULL);
00820 continue;
00821 }
00822
00823 assert(Vehicle::Get(vehicle_index) == v);
00824
00825 switch (v->type) {
00826 default: break;
00827
00828 case VEH_TRAIN:
00829 case VEH_ROAD:
00830 case VEH_AIRCRAFT:
00831 case VEH_SHIP:
00832 if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
00833
00834 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00835 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00836
00837 v->travel_time++;
00838
00839 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00840
00841 v->motion_counter += v->cur_speed;
00842
00843 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00844
00845
00846 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00847 }
00848 }
00849
00850 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00851 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00852 v = it->first;
00853
00854 cur_company.Change(v->owner);
00855
00856
00857
00858
00859 if (it->second) v->vehstatus &= ~VS_STOPPED;
00860
00861
00862 int x = v->x_pos;
00863 int y = v->y_pos;
00864 int z = v->z_pos;
00865
00866 const Company *c = Company::Get(_current_company);
00867 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00868 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00869 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00870
00871 if (!IsLocalCompany()) continue;
00872
00873 if (res.Succeeded()) {
00874 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00875 continue;
00876 }
00877
00878 StringID error_message = res.GetErrorMessage();
00879 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00880
00881 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00882
00883 StringID message;
00884 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00885 message = error_message;
00886 } else {
00887 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00888 }
00889
00890 SetDParam(0, v->index);
00891 SetDParam(1, error_message);
00892 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00893 }
00894
00895 cur_company.Restore();
00896 }
00897
00902 static void DoDrawVehicle(const Vehicle *v)
00903 {
00904 SpriteID image = v->cur_image;
00905 PaletteID pal = PAL_NONE;
00906
00907 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00908
00909
00910 bool shadowed = (v->vehstatus & VS_SHADOW);
00911
00912 if (v->type == VEH_EFFECT) {
00913
00914
00915 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00916 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00917 }
00918
00919 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00920 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed);
00921 }
00922
00927 void ViewportAddVehicles(DrawPixelInfo *dpi)
00928 {
00929
00930 const int l = dpi->left;
00931 const int r = dpi->left + dpi->width;
00932 const int t = dpi->top;
00933 const int b = dpi->top + dpi->height;
00934
00935
00936 int xl, xu, yl, yu;
00937
00938 if (dpi->width + 70 < (1 << (7 + 6))) {
00939 xl = GB(l - 70, 7, 6);
00940 xu = GB(r, 7, 6);
00941 } else {
00942
00943 xl = 0;
00944 xu = 0x3F;
00945 }
00946
00947 if (dpi->height + 70 < (1 << (6 + 6))) {
00948 yl = GB(t - 70, 6, 6) << 6;
00949 yu = GB(b, 6, 6) << 6;
00950 } else {
00951
00952 yl = 0;
00953 yu = 0x3F << 6;
00954 }
00955
00956 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00957 for (int x = xl;; x = (x + 1) & 0x3F) {
00958 const Vehicle *v = _vehicle_position_hash[x + y];
00959
00960 while (v != NULL) {
00961 if (!(v->vehstatus & VS_HIDDEN) &&
00962 l <= v->coord.right &&
00963 t <= v->coord.bottom &&
00964 r >= v->coord.left &&
00965 b >= v->coord.top) {
00966 DoDrawVehicle(v);
00967 }
00968 v = v->next_hash;
00969 }
00970
00971 if (x == xu) break;
00972 }
00973
00974 if (y == yu) break;
00975 }
00976 }
00977
00985 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00986 {
00987 Vehicle *found = NULL, *v;
00988 uint dist, best_dist = UINT_MAX;
00989
00990 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00991
00992 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00993 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00994
00995 FOR_ALL_VEHICLES(v) {
00996 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00997 x >= v->coord.left && x <= v->coord.right &&
00998 y >= v->coord.top && y <= v->coord.bottom) {
00999
01000 dist = max(
01001 abs(((v->coord.left + v->coord.right) >> 1) - x),
01002 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01003 );
01004
01005 if (dist < best_dist) {
01006 found = v;
01007 best_dist = dist;
01008 }
01009 }
01010 }
01011
01012 return found;
01013 }
01014
01019 void DecreaseVehicleValue(Vehicle *v)
01020 {
01021 v->value -= v->value >> 8;
01022 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01023 }
01024
01025 static const byte _breakdown_chance[64] = {
01026 3, 3, 3, 3, 3, 3, 3, 3,
01027 4, 4, 5, 5, 6, 6, 7, 7,
01028 8, 8, 9, 9, 10, 10, 11, 11,
01029 12, 13, 13, 13, 13, 14, 15, 16,
01030 17, 19, 21, 25, 28, 31, 34, 37,
01031 40, 44, 48, 52, 56, 60, 64, 68,
01032 72, 80, 90, 100, 110, 120, 130, 140,
01033 150, 170, 190, 210, 230, 250, 250, 250,
01034 };
01035
01036 void CheckVehicleBreakdown(Vehicle *v)
01037 {
01038 int rel, rel_old;
01039
01040
01041 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01042 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01043
01044 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01045 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01046 v->cur_speed < 5 || _game_mode == GM_MENU) {
01047 return;
01048 }
01049
01050 uint32 r = Random();
01051
01052
01053 int chance = v->breakdown_chance + 1;
01054 if (Chance16I(1, 25, r)) chance += 25;
01055 v->breakdown_chance = min(255, chance);
01056
01057
01058 rel = v->reliability;
01059 if (v->type == VEH_SHIP) rel += 0x6666;
01060
01061
01062 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01063
01064
01065 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01066 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01067 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01068 v->breakdown_chance = 0;
01069 }
01070 }
01071
01078 bool Vehicle::HandleBreakdown()
01079 {
01080
01081
01082
01083
01084
01085 switch (this->breakdown_ctr) {
01086 case 0:
01087 return false;
01088
01089 case 2:
01090 this->breakdown_ctr = 1;
01091
01092 if (this->breakdowns_since_last_service != 255) {
01093 this->breakdowns_since_last_service++;
01094 }
01095
01096 this->MarkDirty();
01097 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01098 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01099
01100 if (this->type == VEH_AIRCRAFT) {
01101
01102 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01103 } else {
01104 this->cur_speed = 0;
01105
01106 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01107 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01108 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01109 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01110 }
01111
01112 if (!(this->vehstatus & VS_HIDDEN)) {
01113 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01114 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01115 }
01116 }
01117
01118 case 1:
01119
01120 if (this->type == VEH_AIRCRAFT) return false;
01121
01122
01123 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01124 if (--this->breakdown_delay == 0) {
01125 this->breakdown_ctr = 0;
01126 this->MarkDirty();
01127 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01128 }
01129 }
01130 return true;
01131
01132 default:
01133 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01134 return false;
01135 }
01136 }
01137
01142 void AgeVehicle(Vehicle *v)
01143 {
01144 if (v->age < MAX_DAY) v->age++;
01145
01146 int age = v->age - v->max_age;
01147 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01148 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01149 v->reliability_spd_dec <<= 1;
01150 }
01151
01152 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01153
01154
01155 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01156
01157
01158 if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
01159
01160 StringID str;
01161 if (age == -DAYS_IN_LEAP_YEAR) {
01162 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01163 } else if (age == 0) {
01164 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01165 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01166 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01167 } else {
01168 return;
01169 }
01170
01171 SetDParam(0, v->index);
01172 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01173 }
01174
01181 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01182 {
01183 int count = 0;
01184 int max = 0;
01185 int cars = 0;
01186 int unloading = 0;
01187 bool loading = false;
01188
01189 const Vehicle *u = v;
01190
01191 const Station *st = Station::GetIfValid(v->last_station_visited);
01192 assert(colour == NULL || st != NULL);
01193
01194
01195 for (; v != NULL; v = v->Next()) {
01196 count += v->cargo.Count();
01197 max += v->cargo_cap;
01198 if (v->cargo_cap != 0 && colour != NULL) {
01199 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01200 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01201 cars++;
01202 }
01203 }
01204
01205 if (colour != NULL) {
01206 if (unloading == 0 && loading) {
01207 *colour = STR_PERCENT_UP;
01208 } else if (cars == unloading || !loading) {
01209 *colour = STR_PERCENT_DOWN;
01210 } else {
01211 *colour = STR_PERCENT_UP_DOWN;
01212 }
01213 }
01214
01215
01216 if (max == 0) return 100;
01217
01218
01219 return (count * 100) / max;
01220 }
01221
01226 void VehicleEnterDepot(Vehicle *v)
01227 {
01228
01229 assert(v == v->First());
01230
01231 switch (v->type) {
01232 case VEH_TRAIN: {
01233 Train *t = Train::From(v);
01234 SetWindowClassesDirty(WC_TRAINS_LIST);
01235
01236 SetDepotReservation(t->tile, false);
01237 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01238
01239 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01240 t->wait_counter = 0;
01241 t->force_proceed = TFP_NONE;
01242 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01243 t->ConsistChanged(true);
01244 break;
01245 }
01246
01247 case VEH_ROAD:
01248 SetWindowClassesDirty(WC_ROADVEH_LIST);
01249 break;
01250
01251 case VEH_SHIP: {
01252 SetWindowClassesDirty(WC_SHIPS_LIST);
01253 Ship *ship = Ship::From(v);
01254 ship->state = TRACK_BIT_DEPOT;
01255 ship->UpdateCache();
01256 ship->UpdateViewport(true, true);
01257 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01258 break;
01259 }
01260
01261 case VEH_AIRCRAFT:
01262 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01263 HandleAircraftEnterHangar(Aircraft::From(v));
01264 break;
01265 default: NOT_REACHED();
01266 }
01267 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01268
01269 if (v->type != VEH_TRAIN) {
01270
01271
01272 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01273 }
01274 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01275
01276 v->vehstatus |= VS_HIDDEN;
01277 v->cur_speed = 0;
01278
01279 VehicleServiceInDepot(v);
01280
01281 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01282
01283 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01284 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01285
01286 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01287 Order t = v->current_order;
01288 v->current_order.MakeDummy();
01289
01290
01291
01292 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01293 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01294 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01295
01296 return;
01297 }
01298
01299 if (t.IsRefit()) {
01300 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01301 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01302 cur_company.Restore();
01303
01304 if (cost.Failed()) {
01305 _vehicles_to_autoreplace[v] = false;
01306 if (v->owner == _local_company) {
01307
01308 SetDParam(0, v->index);
01309 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01310 }
01311 } else if (cost.GetCost() != 0) {
01312 v->profit_this_year -= cost.GetCost() << 8;
01313 if (v->owner == _local_company) {
01314 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01315 }
01316 }
01317 }
01318
01319 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01320
01321 v->DeleteUnreachedImplicitOrders();
01322 UpdateVehicleTimetable(v, true);
01323 v->IncrementImplicitOrderIndex();
01324 }
01325 if (t.GetDepotActionType() & ODATFB_HALT) {
01326
01327 _vehicles_to_autoreplace[v] = false;
01328 if (v->owner == _local_company) {
01329 SetDParam(0, v->index);
01330 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01331 }
01332 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01333 }
01334 }
01335 }
01336
01337
01345 void VehicleMove(Vehicle *v, bool update_viewport)
01346 {
01347 int img = v->cur_image;
01348 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01349 const Sprite *spr = GetSprite(img, ST_NORMAL);
01350
01351 pt.x += spr->x_offs;
01352 pt.y += spr->y_offs;
01353
01354 UpdateVehiclePosHash(v, pt.x, pt.y);
01355
01356 Rect old_coord = v->coord;
01357 v->coord.left = pt.x;
01358 v->coord.top = pt.y;
01359 v->coord.right = pt.x + spr->width + 2;
01360 v->coord.bottom = pt.y + spr->height + 2;
01361
01362 if (update_viewport) {
01363 MarkAllViewportsDirty(
01364 min(old_coord.left, v->coord.left),
01365 min(old_coord.top, v->coord.top),
01366 max(old_coord.right, v->coord.right) + 1,
01367 max(old_coord.bottom, v->coord.bottom) + 1
01368 );
01369 }
01370 }
01371
01380 void MarkSingleVehicleDirty(const Vehicle *v)
01381 {
01382 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01383 }
01384
01390 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01391 {
01392 static const int8 _delta_coord[16] = {
01393 -1,-1,-1, 0, 1, 1, 1, 0,
01394 -1, 0, 1, 1, 1, 0,-1,-1,
01395 };
01396
01397 int x = v->x_pos + _delta_coord[v->direction];
01398 int y = v->y_pos + _delta_coord[v->direction + 8];
01399
01400 GetNewVehiclePosResult gp;
01401 gp.x = x;
01402 gp.y = y;
01403 gp.old_tile = v->tile;
01404 gp.new_tile = TileVirtXY(x, y);
01405 return gp;
01406 }
01407
01408 static const Direction _new_direction_table[] = {
01409 DIR_N, DIR_NW, DIR_W,
01410 DIR_NE, DIR_SE, DIR_SW,
01411 DIR_E, DIR_SE, DIR_S
01412 };
01413
01414 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01415 {
01416 int i = 0;
01417
01418 if (y >= v->y_pos) {
01419 if (y != v->y_pos) i += 3;
01420 i += 3;
01421 }
01422
01423 if (x >= v->x_pos) {
01424 if (x != v->x_pos) i++;
01425 i++;
01426 }
01427
01428 Direction dir = v->direction;
01429
01430 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01431 if (dirdiff == DIRDIFF_SAME) return dir;
01432 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01433 }
01434
01444 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01445 {
01446 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01447 }
01448
01456 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01457 {
01458
01459 const Vehicle *v;
01460 FOR_ALL_VEHICLES(v) {
01461 if (v->type == type && v->owner == owner) {
01462 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01463 }
01464 }
01465
01466 if (this->maxid == 0) return;
01467
01468
01469
01470
01471 this->cache = CallocT<bool>(this->maxid + 2);
01472
01473
01474 FOR_ALL_VEHICLES(v) {
01475 if (v->type == type && v->owner == owner) {
01476 this->cache[v->unitnumber] = true;
01477 }
01478 }
01479 }
01480
01482 UnitID FreeUnitIDGenerator::NextID()
01483 {
01484 if (this->maxid <= this->curid) return ++this->curid;
01485
01486 while (this->cache[++this->curid]) { }
01487
01488 return this->curid;
01489 }
01490
01496 UnitID GetFreeUnitNumber(VehicleType type)
01497 {
01498
01499 uint max_veh;
01500 switch (type) {
01501 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01502 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01503 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01504 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01505 default: NOT_REACHED();
01506 }
01507
01508 uint amounts[4];
01509 CountCompanyVehicles(_current_company, amounts);
01510 assert((uint)type < lengthof(amounts));
01511 if (amounts[type] >= max_veh) return UINT16_MAX;
01512
01513 FreeUnitIDGenerator gen(type, _current_company);
01514
01515 return gen.NextID();
01516 }
01517
01518
01527 bool CanBuildVehicleInfrastructure(VehicleType type)
01528 {
01529 assert(IsCompanyBuildableVehicleType(type));
01530
01531 if (!Company::IsValidID(_local_company)) return false;
01532 if (!_settings_client.gui.disable_unsuitable_building) return true;
01533
01534 UnitID max;
01535 switch (type) {
01536 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01537 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01538 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01539 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01540 default: NOT_REACHED();
01541 }
01542
01543
01544 if (max > 0) {
01545
01546 const Engine *e;
01547 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01548 if (HasBit(e->company_avail, _local_company)) return true;
01549 }
01550 return false;
01551 }
01552
01553
01554 const Vehicle *v;
01555 FOR_ALL_VEHICLES(v) {
01556 if (v->owner == _local_company && v->type == type) return true;
01557 }
01558
01559 return false;
01560 }
01561
01562
01570 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01571 {
01572 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01573 const Engine *e = Engine::Get(engine_type);
01574 switch (e->type) {
01575 default: NOT_REACHED();
01576 case VEH_TRAIN:
01577 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01578
01579
01580 engine_type = parent_engine_type;
01581 e = Engine::Get(engine_type);
01582
01583 }
01584
01585 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01586 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01587 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01588 if (!CargoSpec::Get(cargo_type)->is_freight) {
01589 if (parent_engine_type == INVALID_ENGINE) {
01590 return LS_PASSENGER_WAGON_STEAM;
01591 } else {
01592 switch (RailVehInfo(parent_engine_type)->engclass) {
01593 default: NOT_REACHED();
01594 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01595 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01596 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01597 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01598 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01599 }
01600 }
01601 } else {
01602 return LS_FREIGHT_WAGON;
01603 }
01604 } else {
01605 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01606
01607 switch (e->u.rail.engclass) {
01608 default: NOT_REACHED();
01609 case EC_STEAM: return LS_STEAM;
01610 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01611 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01612 case EC_MONORAIL: return LS_MONORAIL;
01613 case EC_MAGLEV: return LS_MAGLEV;
01614 }
01615 }
01616
01617 case VEH_ROAD:
01618
01619 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01620 engine_type = parent_engine_type;
01621 e = Engine::Get(engine_type);
01622 cargo_type = v->First()->cargo_type;
01623 }
01624 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01625 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01626
01627
01628 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01629
01630 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01631 } else {
01632
01633 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01634 }
01635
01636 case VEH_SHIP:
01637 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01638 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01639 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01640
01641 case VEH_AIRCRAFT:
01642 switch (e->u.air.subtype) {
01643 case AIR_HELI: return LS_HELICOPTER;
01644 case AIR_CTOL: return LS_SMALL_PLANE;
01645 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01646 default: NOT_REACHED();
01647 }
01648 }
01649 }
01650
01660 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01661 {
01662 const Company *c = Company::Get(company);
01663 LiveryScheme scheme = LS_DEFAULT;
01664
01665
01666
01667 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01668
01669 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01670
01671
01672 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01673 }
01674
01675 return &c->livery[scheme];
01676 }
01677
01678
01679 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01680 {
01681 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01682
01683
01684 if (map != PAL_NONE) return map;
01685
01686 const Engine *e = Engine::Get(engine_type);
01687
01688
01689 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01690 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01691
01692 if (callback != CALLBACK_FAILED) {
01693 assert_compile(PAL_NONE == 0);
01694 map = GB(callback, 0, 14);
01695
01696
01697 if (!HasBit(callback, 14)) {
01698
01699 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01700 return map;
01701 }
01702 }
01703 }
01704
01705 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01706
01707 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01708
01709
01710 if (!Company::IsValidID(company)) return map;
01711
01712 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01713
01714 map += livery->colour1;
01715 if (twocc) map += livery->colour2 * 16;
01716
01717
01718 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01719 return map;
01720 }
01721
01728 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01729 {
01730 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01731 }
01732
01738 PaletteID GetVehiclePalette(const Vehicle *v)
01739 {
01740 if (v->IsGroundVehicle()) {
01741 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01742 }
01743
01744 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01745 }
01746
01755 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01756 {
01757 if (mail_capacity != NULL) *mail_capacity = 0;
01758 const Engine *e = Engine::Get(v->engine_type);
01759
01760 if (!e->CanCarryCargo()) return 0;
01761
01762 if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01763 *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01764 }
01765 CargoID default_cargo = e->GetDefaultCargoType();
01766
01767
01768
01769 if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01770 (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01771 uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01772 if (callback != CALLBACK_FAILED) return callback;
01773 }
01774
01775
01776 uint capacity;
01777 switch (e->type) {
01778 case VEH_TRAIN: capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY, e->u.rail.capacity); break;
01779 case VEH_ROAD: capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
01780 case VEH_SHIP: capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY, e->u.ship.capacity); break;
01781 case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
01782 default: NOT_REACHED();
01783 }
01784
01785
01786
01787 if (e->type != VEH_SHIP) {
01788 if (e->type == VEH_AIRCRAFT) {
01789 if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01790 capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01791 }
01792 if (v->cargo_type == CT_MAIL) return capacity;
01793 } else {
01794 switch (default_cargo) {
01795 case CT_PASSENGERS: break;
01796 case CT_MAIL:
01797 case CT_GOODS: capacity *= 2; break;
01798 default: capacity *= 4; break;
01799 }
01800 }
01801 switch (v->cargo_type) {
01802 case CT_PASSENGERS: break;
01803 case CT_MAIL:
01804 case CT_GOODS: capacity /= 2; break;
01805 default: capacity /= 4; break;
01806 }
01807 }
01808
01809 return capacity;
01810 }
01811
01815 void Vehicle::DeleteUnreachedImplicitOrders()
01816 {
01817 if (this->IsGroundVehicle()) {
01818 uint16 &gv_flags = this->GetGroundVehicleFlags();
01819 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01820
01821 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01822 this->cur_implicit_order_index = this->cur_real_order_index;
01823 InvalidateVehicleOrder(this, 0);
01824 return;
01825 }
01826 }
01827
01828 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01829 while (order != NULL) {
01830 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01831
01832 if (order->IsType(OT_IMPLICIT)) {
01833
01834 order = order->next;
01835 DeleteOrder(this, this->cur_implicit_order_index);
01836 } else {
01837
01838 order = order->next;
01839 this->cur_implicit_order_index++;
01840 }
01841
01842
01843 if (order == NULL) {
01844 order = this->GetOrder(0);
01845 this->cur_implicit_order_index = 0;
01846 }
01847 }
01848 }
01849
01855 void Vehicle::BeginLoading(StationID station)
01856 {
01857 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01858
01859 this->last_station_visited = station;
01860
01861 if (this->current_order.IsType(OT_GOTO_STATION) &&
01862 this->current_order.GetDestination() == this->last_station_visited) {
01863 this->DeleteUnreachedImplicitOrders();
01864
01865
01866 this->current_order.MakeLoading(true);
01867 UpdateVehicleTimetable(this, true);
01868
01869
01870
01871
01872
01873
01874 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01875
01876 } else {
01877
01878
01879
01880
01881
01882 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01883 if (this->IsGroundVehicle() && in_list != NULL &&
01884 (!in_list->IsType(OT_IMPLICIT) ||
01885 in_list->GetDestination() != this->last_station_visited)) {
01886 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01887
01888 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01889 if (prev_order == NULL ||
01890 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01891 prev_order->GetDestination() != this->last_station_visited) {
01892
01893
01894
01895 int target_index = this->cur_implicit_order_index;
01896 bool found = false;
01897 while (target_index != this->cur_real_order_index) {
01898 const Order *order = this->GetOrder(target_index);
01899 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01900 found = true;
01901 break;
01902 }
01903 target_index++;
01904 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01905 assert(target_index != this->cur_implicit_order_index);
01906 }
01907
01908 if (found) {
01909 if (suppress_implicit_orders) {
01910
01911 this->cur_implicit_order_index = target_index;
01912 InvalidateVehicleOrder(this, 0);
01913 } else {
01914
01915 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01916 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01917 if (order->IsType(OT_IMPLICIT)) {
01918
01919 order = order->next;
01920 DeleteOrder(this, this->cur_implicit_order_index);
01921 } else {
01922
01923 order = order->next;
01924 this->cur_implicit_order_index++;
01925 }
01926
01927
01928 if (order == NULL) {
01929 order = this->GetOrder(0);
01930 this->cur_implicit_order_index = 0;
01931 }
01932 assert(order != NULL);
01933 }
01934 }
01935 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01936
01937 Order *implicit_order = new Order();
01938 implicit_order->MakeImplicit(this->last_station_visited);
01939 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01940 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01941 this->current_order.index = implicit_order->index;
01942
01943
01944
01945 uint16 &gv_flags = this->GetGroundVehicleFlags();
01946 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01947 }
01948 }
01949 }
01950 this->current_order.MakeLoading(false);
01951 }
01952
01953 UpdateVehicleRouteLinks(this, station);
01954
01955
01956
01957 this->last_order_id = this->current_order.index;
01958 this->last_station_loaded = station;
01959
01960 Station *last_visited = Station::Get(this->last_station_visited);
01961 last_visited->loading_vehicles.push_back(this);
01962
01963
01964 CargoID cid;
01965 FOR_EACH_SET_CARGO_ID(cid, this->vcache.cached_cargo_mask) {
01966
01967 if (CargoHasDestinations(cid) && last_visited->goods[cid].cargo_counter == 0) {
01968 last_visited->goods[cid].cargo.UpdateCargoNextHop(last_visited, cid);
01969 last_visited->goods[cid].cargo_counter = _settings_game.economy.cargodest.route_recalc_delay;
01970 }
01971 }
01972
01973 PrepareUnload(this);
01974
01975 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01976 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01977 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01978 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01979
01980 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01981 this->cur_speed = 0;
01982 this->MarkDirty();
01983 }
01984
01989 void Vehicle::LeaveStation()
01990 {
01991 assert(this->current_order.IsType(OT_LOADING));
01992
01993 delete this->cargo_payment;
01994
01995
01996 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01997
01998
01999 this->travel_time = 0;
02000
02001 this->current_order.MakeLeaveStation();
02002 Station *st = Station::Get(this->last_station_visited);
02003 st->loading_vehicles.remove(this);
02004
02005 HideFillingPercent(&this->fill_percent_te_id);
02006
02007 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02008
02009 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02010
02011 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02012 }
02013 }
02014
02015
02021 void Vehicle::HandleLoading(bool mode)
02022 {
02023 switch (this->current_order.GetType()) {
02024 case OT_LOADING: {
02025 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02026
02027
02028 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02029
02030 this->PlayLeaveStationSound();
02031
02032 this->LeaveStation();
02033
02034
02035 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02036 if (order == NULL ||
02037 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02038 order->GetDestination() != this->last_station_visited) {
02039 return;
02040 }
02041 break;
02042 }
02043
02044 case OT_DUMMY: break;
02045
02046 default: return;
02047 }
02048
02049 this->IncrementImplicitOrderIndex();
02050 }
02051
02058 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02059 {
02060 CommandCost ret = CheckOwnership(this->owner);
02061 if (ret.Failed()) return ret;
02062
02063 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02064 if (this->IsStoppedInDepot()) return CMD_ERROR;
02065
02066 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02067 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02068 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02069
02070
02071
02072 if (flags & DC_EXEC) {
02073 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02074 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02075 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02076 }
02077 return CommandCost();
02078 }
02079
02080 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02081 if (flags & DC_EXEC) {
02082
02083
02084 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02085
02086 if (this->IsGroundVehicle()) {
02087 uint16 &gv_flags = this->GetGroundVehicleFlags();
02088 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02089 }
02090
02091 this->current_order.MakeDummy();
02092 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02093 }
02094 return CommandCost();
02095 }
02096
02097 TileIndex location;
02098 DestinationID destination;
02099 bool reverse;
02100 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};
02101 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02102
02103 if (flags & DC_EXEC) {
02104 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02105
02106 if (this->IsGroundVehicle()) {
02107 uint16 &gv_flags = this->GetGroundVehicleFlags();
02108 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02109 }
02110
02111 this->dest_tile = location;
02112 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02113 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02114 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02115
02116
02117 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02118
02119 if (this->type == VEH_AIRCRAFT) {
02120 Aircraft *a = Aircraft::From(this);
02121 if (a->state == FLYING && a->targetairport != destination) {
02122
02123 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02124 AircraftNextAirportPos_and_Order(a);
02125 }
02126 }
02127 }
02128
02129 return CommandCost();
02130
02131 }
02132
02137 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02138 {
02139 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02140 const Engine *e = Engine::Get(this->engine_type);
02141
02142
02143 byte visual_effect;
02144 switch (e->type) {
02145 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02146 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02147 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02148 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02149 }
02150
02151
02152 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02153 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02154
02155 if (callback != CALLBACK_FAILED) {
02156 callback = GB(callback, 0, 8);
02157
02158
02159 if (callback == VE_DEFAULT) {
02160 assert(HasBit(callback, VE_DISABLE_EFFECT));
02161 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02162 }
02163 visual_effect = callback;
02164 }
02165 }
02166
02167
02168 if (visual_effect == VE_DEFAULT ||
02169 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02170
02171
02172 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02173 if (visual_effect == VE_DEFAULT) {
02174 visual_effect = 1 << VE_DISABLE_EFFECT;
02175 } else {
02176 SetBit(visual_effect, VE_DISABLE_EFFECT);
02177 }
02178 } else {
02179 if (visual_effect == VE_DEFAULT) {
02180
02181 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02182 }
02183 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02184 }
02185 }
02186
02187 this->vcache.cached_vis_effect = visual_effect;
02188
02189 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02190 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02191 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02192 }
02193 }
02194
02195 static const int8 _vehicle_smoke_pos[8] = {
02196 1, 1, 1, 0, -1, -1, -1, 0
02197 };
02198
02203 void Vehicle::ShowVisualEffect() const
02204 {
02205 assert(this->IsPrimaryVehicle());
02206 bool sound = false;
02207
02208
02209
02210
02211
02212
02213 if (_settings_game.vehicle.smoke_amount == 0 ||
02214 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02215 this->cur_speed < 2) {
02216 return;
02217 }
02218 if (this->type == VEH_TRAIN) {
02219 const Train *t = Train::From(this);
02220
02221
02222
02223
02224 if (HasBit(t->flags, VRF_REVERSING) ||
02225 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02226 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02227 return;
02228 }
02229 }
02230
02231 const Vehicle *v = this;
02232
02233 do {
02234 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02235 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02236 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02237
02238
02239
02240
02241
02242
02243
02244
02245 if (disable_effect ||
02246 v->vehstatus & VS_HIDDEN ||
02247 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02248 IsDepotTile(v->tile) ||
02249 IsTunnelTile(v->tile) ||
02250 (v->type == VEH_TRAIN &&
02251 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02252 continue;
02253 }
02254
02255 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02256 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02257
02258 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02259 x = -x;
02260 y = -y;
02261 }
02262
02263 switch (effect_type) {
02264 case VE_TYPE_STEAM:
02265
02266
02267
02268
02269
02270 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02271 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02272 sound = true;
02273 }
02274 break;
02275
02276 case VE_TYPE_DIESEL: {
02277
02278
02279
02280
02281
02282
02283
02284
02285
02286
02287
02288 int power_weight_effect = 0;
02289 if (v->type == VEH_TRAIN) {
02290 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02291 }
02292 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02293 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02294 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02295 sound = true;
02296 }
02297 break;
02298 }
02299
02300 case VE_TYPE_ELECTRIC:
02301
02302
02303
02304
02305
02306
02307 if (GB(v->tick_counter, 0, 2) == 0 &&
02308 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02309 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02310 sound = true;
02311 }
02312 break;
02313
02314 default:
02315 break;
02316 }
02317 } while ((v = v->Next()) != NULL);
02318
02319 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02320 }
02321
02326 void Vehicle::SetNext(Vehicle *next)
02327 {
02328 assert(this != next);
02329
02330 if (this->next != NULL) {
02331
02332 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02333 v->first = this->next;
02334 }
02335 this->next->previous = NULL;
02336 }
02337
02338 this->next = next;
02339
02340 if (this->next != NULL) {
02341
02342 if (this->next->previous != NULL) this->next->previous->next = NULL;
02343 this->next->previous = this;
02344 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02345 v->first = this->first;
02346 }
02347 }
02348 }
02349
02355 void Vehicle::AddToShared(Vehicle *shared_chain)
02356 {
02357 assert(this->previous_shared == NULL && this->next_shared == NULL);
02358
02359 if (shared_chain->orders.list == NULL) {
02360 assert(shared_chain->previous_shared == NULL);
02361 assert(shared_chain->next_shared == NULL);
02362 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02363 }
02364
02365 this->next_shared = shared_chain->next_shared;
02366 this->previous_shared = shared_chain;
02367
02368 shared_chain->next_shared = this;
02369
02370 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02371
02372 shared_chain->orders.list->AddVehicle(this);
02373 }
02374
02378 void Vehicle::RemoveFromShared()
02379 {
02380
02381
02382 bool were_first = (this->FirstShared() == this);
02383 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02384
02385 this->orders.list->RemoveVehicle(this);
02386
02387 if (!were_first) {
02388
02389 this->previous_shared->next_shared = this->NextShared();
02390 }
02391
02392 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02393
02394
02395 if (this->orders.list->GetNumVehicles() == 1) {
02396
02397 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02398 InvalidateVehicleOrder(this->FirstShared(), 0);
02399 } else if (were_first) {
02400
02401
02402 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02403 }
02404
02405 this->next_shared = NULL;
02406 this->previous_shared = NULL;
02407 }
02408
02409 void VehiclesYearlyLoop()
02410 {
02411 Vehicle *v;
02412 FOR_ALL_VEHICLES(v) {
02413 if (v->IsPrimaryVehicle()) {
02414
02415 Money profit = v->GetDisplayProfitThisYear();
02416 if (v->age >= 730 && profit < 0) {
02417 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02418 SetDParam(0, v->index);
02419 SetDParam(1, profit);
02420 AddVehicleNewsItem(
02421 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02422 NS_ADVICE,
02423 v->index
02424 );
02425 }
02426 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02427 }
02428
02429 v->profit_last_year = v->profit_this_year;
02430 v->profit_this_year = 0;
02431 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02432 }
02433 }
02434 }
02435
02436
02446 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02447 {
02448 const Engine *e = Engine::GetIfValid(engine_type);
02449 assert(e != NULL);
02450
02451 switch (e->type) {
02452 case VEH_TRAIN:
02453 return (st->facilities & FACIL_TRAIN) != 0;
02454
02455 case VEH_ROAD:
02456
02457
02458
02459 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02460
02461 case VEH_SHIP:
02462 return (st->facilities & FACIL_DOCK) != 0;
02463
02464 case VEH_AIRCRAFT:
02465 return (st->facilities & FACIL_AIRPORT) != 0 &&
02466 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02467
02468 default:
02469 return false;
02470 }
02471 }
02472
02479 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02480 {
02481 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02482
02483 return CanVehicleUseStation(v->engine_type, st);
02484 }
02485
02491 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02492 {
02493 assert(this->IsGroundVehicle());
02494 if (this->type == VEH_TRAIN) {
02495 return &Train::From(this)->gcache;
02496 } else {
02497 return &RoadVehicle::From(this)->gcache;
02498 }
02499 }
02500
02506 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02507 {
02508 assert(this->IsGroundVehicle());
02509 if (this->type == VEH_TRAIN) {
02510 return &Train::From(this)->gcache;
02511 } else {
02512 return &RoadVehicle::From(this)->gcache;
02513 }
02514 }
02515
02521 uint16 &Vehicle::GetGroundVehicleFlags()
02522 {
02523 assert(this->IsGroundVehicle());
02524 if (this->type == VEH_TRAIN) {
02525 return Train::From(this)->gv_flags;
02526 } else {
02527 return RoadVehicle::From(this)->gv_flags;
02528 }
02529 }
02530
02536 const uint16 &Vehicle::GetGroundVehicleFlags() const
02537 {
02538 assert(this->IsGroundVehicle());
02539 if (this->type == VEH_TRAIN) {
02540 return Train::From(this)->gv_flags;
02541 } else {
02542 return RoadVehicle::From(this)->gv_flags;
02543 }
02544 }
02545
02554 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02555 {
02556 if (v->type == VEH_TRAIN) {
02557 Train *u = Train::From(v);
02558
02559 u = u->GetFirstEnginePart();
02560
02561
02562 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02563 do {
02564
02565 set.Include(u->index);
02566
02567
02568 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02569
02570 u = u->Next();
02571 } while (u != NULL && u->IsArticulatedPart());
02572 }
02573 }
02574 }