00001
00002
00005 #include "stdafx.h"
00006 #include "gui.h"
00007 #include "openttd.h"
00008 #include "debug.h"
00009 #include "roadveh.h"
00010 #include "ship.h"
00011 #include "spritecache.h"
00012 #include "landscape.h"
00013 #include "timetable.h"
00014 #include "viewport_func.h"
00015 #include "news_func.h"
00016 #include "command_func.h"
00017 #include "company_func.h"
00018 #include "vehicle_gui.h"
00019 #include "train.h"
00020 #include "aircraft.h"
00021 #include "newgrf_engine.h"
00022 #include "newgrf_sound.h"
00023 #include "newgrf_station.h"
00024 #include "group.h"
00025 #include "group_gui.h"
00026 #include "strings_func.h"
00027 #include "zoom_func.h"
00028 #include "functions.h"
00029 #include "date_func.h"
00030 #include "window_func.h"
00031 #include "vehicle_func.h"
00032 #include "autoreplace_func.h"
00033 #include "autoreplace_gui.h"
00034 #include "oldpool_func.h"
00035 #include "ai/ai.hpp"
00036 #include "core/smallmap_type.hpp"
00037 #include "depot_func.h"
00038 #include "settings_type.h"
00039 #include "network/network.h"
00040
00041 #include "economy_base.h"
00042 #include "table/sprites.h"
00043 #include "table/strings.h"
00044
00045 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00046
00047 VehicleID _vehicle_id_ctr_day;
00048 const Vehicle *_place_clicked_vehicle;
00049 VehicleID _new_vehicle_id;
00050 uint16 _returned_refit_capacity;
00051
00052
00053
00054 DEFINE_OLD_POOL_GENERIC(Vehicle, Vehicle)
00055
00056
00060 bool Vehicle::NeedsAutorenewing(const Company *c) const
00061 {
00062
00063
00064
00065
00066 assert(c == GetCompany(this->owner));
00067
00068 if (!c->engine_renew) return false;
00069 if (this->age - this->max_age < (c->engine_renew_months * 30)) return false;
00070 if (this->age == 0) return false;
00071
00072 return true;
00073 }
00074
00075 void VehicleServiceInDepot(Vehicle *v)
00076 {
00077 v->date_of_last_service = _date;
00078 v->breakdowns_since_last_service = 0;
00079 v->reliability = GetEngine(v->engine_type)->reliability;
00080 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00081 }
00082
00083 bool Vehicle::NeedsServicing() const
00084 {
00085 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00086
00087 if (_settings_game.order.no_servicing_if_no_breakdowns && _settings_game.difficulty.vehicle_breakdowns == 0) {
00088
00089
00090 return EngineHasReplacementForCompany(GetCompany(this->owner), this->engine_type, this->group_id);
00091 }
00092
00093 return _settings_game.vehicle.servint_ispercent ?
00094 (this->reliability < GetEngine(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00095 (this->date_of_last_service + this->service_interval < _date);
00096 }
00097
00098 bool Vehicle::NeedsAutomaticServicing() const
00099 {
00100 if (_settings_game.order.gotodepot && VehicleHasDepotOrders(this)) return false;
00101 if (this->current_order.IsType(OT_LOADING)) return false;
00102 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00103 return NeedsServicing();
00104 }
00105
00114 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00115 {
00116 const Engine *e = GetEngine(engine);
00117 uint32 grfid = e->grffile->grfid;
00118 GRFConfig *grfconfig = GetGRFConfig(grfid);
00119
00120 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00121 SetBit(grfconfig->grf_bugs, bug_type);
00122 SetDParamStr(0, grfconfig->name);
00123 SetDParam(1, engine);
00124 ShowErrorMessage(part2, part1, 0, 0);
00125 if (!_networking) _pause_game = (critical ? -1 : 1);
00126 }
00127
00128
00129 char buffer[512];
00130
00131 SetDParamStr(0, grfconfig->name);
00132 GetString(buffer, part1, lastof(buffer));
00133 DEBUG(grf, 0, "%s", buffer + 3);
00134
00135 SetDParam(1, engine);
00136 GetString(buffer, part2, lastof(buffer));
00137 DEBUG(grf, 0, "%s", buffer + 3);
00138 }
00139
00140 StringID VehicleInTheWayErrMsg(const Vehicle *v)
00141 {
00142 switch (v->type) {
00143 case VEH_TRAIN: return STR_8803_TRAIN_IN_THE_WAY;
00144 case VEH_ROAD: return STR_9000_ROAD_VEHICLE_IN_THE_WAY;
00145 case VEH_AIRCRAFT: return STR_A015_AIRCRAFT_IN_THE_WAY;
00146 default: return STR_980E_SHIP_IN_THE_WAY;
00147 }
00148 }
00149
00150 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00151 {
00152 byte z = *(byte*)data;
00153
00154 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00155 if (v->z_pos > z) return NULL;
00156
00157 _error_message = VehicleInTheWayErrMsg(v);
00158 return v;
00159 }
00160
00161 bool EnsureNoVehicleOnGround(TileIndex tile)
00162 {
00163 byte z = GetTileMaxZ(tile);
00164 return !HasVehicleOnPos(tile, &z, &EnsureNoVehicleProcZ);
00165 }
00166
00168 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00169 {
00170 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00171 if (v == (const Vehicle *)data) return NULL;
00172
00173 _error_message = VehicleInTheWayErrMsg(v);
00174 return v;
00175 }
00176
00184 bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00185 {
00186 return HasVehicleOnPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc) ||
00187 HasVehicleOnPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc);
00188 }
00189
00190
00191 Vehicle::Vehicle()
00192 {
00193 this->type = VEH_INVALID;
00194 this->coord.left = INVALID_COORD;
00195 this->group_id = DEFAULT_GROUP;
00196 this->fill_percent_te_id = INVALID_TE_ID;
00197 this->first = this;
00198 this->colourmap = PAL_NONE;
00199 }
00200
00205 byte VehicleRandomBits()
00206 {
00207 return GB(Random(), 0, 8);
00208 }
00209
00210
00211 bool Vehicle::AllocateList(Vehicle **vl, int num)
00212 {
00213 if (!Vehicle::CanAllocateItem(num)) return false;
00214 if (vl == NULL) return true;
00215
00216 uint counter = _Vehicle_pool.first_free_index;
00217
00218 for (int i = 0; i != num; i++) {
00219 vl[i] = new (AllocateRaw(counter)) InvalidVehicle();
00220 counter++;
00221 }
00222
00223 return true;
00224 }
00225
00226
00227
00228 const int HASH_BITS = 7;
00229 const int HASH_SIZE = 1 << HASH_BITS;
00230 const int HASH_MASK = HASH_SIZE - 1;
00231 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00232 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00233
00234
00235
00236 const int HASH_RES = 0;
00237
00238 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00239
00240 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00241 {
00242 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00243 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00244 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00245 for (; v != NULL; v = v->next_new_hash) {
00246 Vehicle *a = proc(v, data);
00247 if (find_first && a != NULL) return a;
00248 }
00249 if (x == xu) break;
00250 }
00251 if (y == yu) break;
00252 }
00253
00254 return NULL;
00255 }
00256
00257
00269 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00270 {
00271 const int COLL_DIST = 6;
00272
00273
00274 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00275 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00276 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00277 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00278
00279 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00280 }
00281
00296 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00297 {
00298 VehicleFromPosXY(x, y, data, proc, false);
00299 }
00300
00312 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00313 {
00314 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00315 }
00316
00327 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00328 {
00329 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00330 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00331
00332 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00333 for (; v != NULL; v = v->next_new_hash) {
00334 if (v->tile != tile) continue;
00335
00336 Vehicle *a = proc(v, data);
00337 if (find_first && a != NULL) return a;
00338 }
00339
00340 return NULL;
00341 }
00342
00356 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00357 {
00358 VehicleFromPos(tile, data, proc, false);
00359 }
00360
00371 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00372 {
00373 return VehicleFromPos(tile, data, proc, true) != NULL;
00374 }
00375
00376
00377 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00378 {
00379 Vehicle **old_hash = v->old_new_hash;
00380 Vehicle **new_hash;
00381
00382 if (remove) {
00383 new_hash = NULL;
00384 } else {
00385 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00386 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00387 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00388 }
00389
00390 if (old_hash == new_hash) return;
00391
00392
00393 if (old_hash != NULL) {
00394 Vehicle *last = NULL;
00395 Vehicle *u = *old_hash;
00396 while (u != v) {
00397 last = u;
00398 u = u->next_new_hash;
00399 assert(u != NULL);
00400 }
00401
00402 if (last == NULL) {
00403 *old_hash = v->next_new_hash;
00404 } else {
00405 last->next_new_hash = v->next_new_hash;
00406 }
00407 }
00408
00409
00410 if (new_hash != NULL) {
00411 v->next_new_hash = *new_hash;
00412 *new_hash = v;
00413 assert(v != v->next_new_hash);
00414 }
00415
00416
00417 v->old_new_hash = new_hash;
00418 }
00419
00420 static Vehicle *_vehicle_position_hash[0x1000];
00421
00422 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00423 {
00424 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00425
00426 Vehicle **old_hash, **new_hash;
00427 int old_x = v->coord.left;
00428 int old_y = v->coord.top;
00429
00430 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00431 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00432
00433 if (old_hash == new_hash) return;
00434
00435
00436 if (old_hash != NULL) {
00437 Vehicle *last = NULL;
00438 Vehicle *u = *old_hash;
00439 while (u != v) {
00440 last = u;
00441 u = u->next_hash;
00442 assert(u != NULL);
00443 }
00444
00445 if (last == NULL) {
00446 *old_hash = v->next_hash;
00447 } else {
00448 last->next_hash = v->next_hash;
00449 }
00450 }
00451
00452
00453 if (new_hash != NULL) {
00454 v->next_hash = *new_hash;
00455 *new_hash = v;
00456 }
00457 }
00458
00459 void ResetVehiclePosHash()
00460 {
00461 Vehicle *v;
00462 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00463 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00464 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00465 }
00466
00467 void ResetVehicleColourMap()
00468 {
00469 Vehicle *v;
00470 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00471 }
00472
00477 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00478 static AutoreplaceMap _vehicles_to_autoreplace;
00479
00480 void InitializeVehicles()
00481 {
00482 _Vehicle_pool.CleanPool();
00483 _Vehicle_pool.AddBlockToPool();
00484
00485 _CargoPayment_pool.CleanPool();
00486 _CargoPayment_pool.AddBlockToPool();
00487
00488 _cargo_payment_savegame = false;
00489
00490 _vehicles_to_autoreplace.Reset();
00491 ResetVehiclePosHash();
00492 }
00493
00494 Vehicle *GetLastVehicleInChain(Vehicle *v)
00495 {
00496 while (v->Next() != NULL) v = v->Next();
00497 return v;
00498 }
00499
00500 const Vehicle *GetLastVehicleInChain(const Vehicle *v)
00501 {
00502 while (v->Next() != NULL) v = v->Next();
00503 return v;
00504 }
00505
00506 uint CountVehiclesInChain(const Vehicle *v)
00507 {
00508 uint count = 0;
00509 do count++; while ((v = v->Next()) != NULL);
00510 return count;
00511 }
00512
00517 bool IsEngineCountable(const Vehicle *v)
00518 {
00519 switch (v->type) {
00520 case VEH_AIRCRAFT: return IsNormalAircraft(v);
00521 case VEH_TRAIN:
00522 return !IsArticulatedPart(v) &&
00523 !IsRearDualheaded(v);
00524 case VEH_ROAD: return IsRoadVehFront(v);
00525 case VEH_SHIP: return true;
00526 default: return false;
00527 }
00528 }
00529
00530 void Vehicle::PreDestructor()
00531 {
00532 if (CleaningPool()) return;
00533
00534 if (IsValidStationID(this->last_station_visited)) {
00535 GetStation(this->last_station_visited)->loading_vehicles.remove(this);
00536
00537 HideFillingPercent(&this->fill_percent_te_id);
00538
00539 delete this->cargo_payment;
00540 }
00541
00542 if (IsEngineCountable(this)) {
00543 GetCompany(this->owner)->num_engines[this->engine_type]--;
00544 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00545
00546 DeleteGroupHighlightOfVehicle(this);
00547 if (IsValidGroupID(this->group_id)) GetGroup(this->group_id)->num_engines[this->engine_type]--;
00548 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00549 }
00550
00551 if (this->type == VEH_ROAD) ClearSlot(this);
00552 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00553 Station *st = GetTargetAirportIfValid(this);
00554 if (st != NULL) {
00555 const AirportFTA *layout = st->Airport()->layout;
00556 CLRBITS(st->airport_flags, layout[this->u.air.previous_pos].block | layout[this->u.air.pos].block);
00557 }
00558 }
00559
00560 if (this->type != VEH_TRAIN || (this->type == VEH_TRAIN && (IsFrontEngine(this) || IsFreeWagon(this)))) {
00561 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00562 }
00563
00564 if (this->IsPrimaryVehicle()) {
00565 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00566 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00567 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00568 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00569 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00570 InvalidateWindow(WC_COMPANY, this->owner);
00571 }
00572 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00573
00574 this->cargo.Truncate(0);
00575 DeleteVehicleOrders(this);
00576 DeleteDepotHighlightOfVehicle(this);
00577
00578 extern void StopGlobalFollowVehicle(const Vehicle *v);
00579 StopGlobalFollowVehicle(this);
00580
00581 ReleaseDisastersTargetingVehicle(this->index);
00582 }
00583
00584 Vehicle::~Vehicle()
00585 {
00586 free(this->name);
00587
00588 if (CleaningPool()) return;
00589
00590
00591
00592 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00593
00594 Vehicle *v = this->Next();
00595 this->SetNext(NULL);
00596
00597 delete v;
00598
00599 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00600 this->next_hash = NULL;
00601 this->next_new_hash = NULL;
00602
00603 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00604
00605 this->type = VEH_INVALID;
00606 }
00607
00611 void VehicleEnteredDepotThisTick(Vehicle *v)
00612 {
00613
00614 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00615
00616
00617
00618
00619
00620
00621 v->vehstatus |= VS_STOPPED;
00622 }
00623
00624 void CallVehicleTicks()
00625 {
00626 _vehicles_to_autoreplace.Clear();
00627
00628 Station *st;
00629 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00630
00631 Vehicle *v;
00632 FOR_ALL_VEHICLES(v) {
00633 v->Tick();
00634
00635 switch (v->type) {
00636 default: break;
00637
00638 case VEH_TRAIN:
00639 case VEH_ROAD:
00640 case VEH_AIRCRAFT:
00641 case VEH_SHIP:
00642 if (v->type == VEH_TRAIN && IsTrainWagon(v)) continue;
00643 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00644 if (v->type == VEH_ROAD && !IsRoadVehFront(v)) continue;
00645
00646 v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed;
00647
00648 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00649
00650
00651 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00652 }
00653 }
00654
00655 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00656 v = it->first;
00657
00658 _current_company = v->owner;
00659
00660
00661
00662
00663 if (it->second) v->vehstatus &= ~VS_STOPPED;
00664
00665
00666 int x = v->x_pos;
00667 int y = v->y_pos;
00668 int z = v->z_pos;
00669
00670 const Company *c = GetCompany(_current_company);
00671 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->engine_renew_money));
00672 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00673 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->engine_renew_money));
00674
00675 if (!IsLocalCompany()) continue;
00676
00677 if (res.Succeeded()) {
00678 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00679 continue;
00680 }
00681
00682 StringID error_message = res.GetErrorMessage();
00683 if (error_message == STR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00684
00685 if (error_message == STR_0003_NOT_ENOUGH_CASH_REQUIRES) error_message = STR_AUTOREPLACE_MONEY_LIMIT;
00686
00687 StringID message;
00688 if (error_message == STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00689 message = error_message;
00690 } else {
00691 message = STR_VEHICLE_AUTORENEW_FAILED;
00692 }
00693
00694 SetDParam(0, v->index);
00695 SetDParam(1, error_message);
00696 AddNewsItem(message, NS_ADVICE, v->index, 0);
00697 }
00698
00699 _current_company = OWNER_NONE;
00700 }
00701
00707 bool CanRefitTo(EngineID engine_type, CargoID cid_to)
00708 {
00709 return HasBit(EngInfo(engine_type)->refit_mask, cid_to);
00710 }
00711
00716 CargoID FindFirstRefittableCargo(EngineID engine_type)
00717 {
00718 uint32 refit_mask = EngInfo(engine_type)->refit_mask;
00719
00720 if (refit_mask != 0) {
00721 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00722 if (HasBit(refit_mask, cid)) return cid;
00723 }
00724 }
00725
00726 return CT_INVALID;
00727 }
00728
00733 CommandCost GetRefitCost(EngineID engine_type)
00734 {
00735 Money base_cost;
00736 ExpensesType expense_type;
00737 switch (GetEngine(engine_type)->type) {
00738 case VEH_SHIP:
00739 base_cost = _price.ship_base;
00740 expense_type = EXPENSES_SHIP_RUN;
00741 break;
00742
00743 case VEH_ROAD:
00744 base_cost = _price.roadveh_base;
00745 expense_type = EXPENSES_ROADVEH_RUN;
00746 break;
00747
00748 case VEH_AIRCRAFT:
00749 base_cost = _price.aircraft_base;
00750 expense_type = EXPENSES_AIRCRAFT_RUN;
00751 break;
00752
00753 case VEH_TRAIN:
00754 base_cost = 2 * ((RailVehInfo(engine_type)->railveh_type == RAILVEH_WAGON) ?
00755 _price.build_railwagon : _price.build_railvehicle);
00756 expense_type = EXPENSES_TRAIN_RUN;
00757 break;
00758
00759 default: NOT_REACHED();
00760 }
00761 return CommandCost(expense_type, (EngInfo(engine_type)->refit_cost * base_cost) >> 10);
00762 }
00763
00764 static void DoDrawVehicle(const Vehicle *v)
00765 {
00766 SpriteID image = v->cur_image;
00767 SpriteID pal = PAL_NONE;
00768
00769 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00770
00771 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00772 v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00773 }
00774
00775 void ViewportAddVehicles(DrawPixelInfo *dpi)
00776 {
00777
00778 const int l = dpi->left;
00779 const int r = dpi->left + dpi->width;
00780 const int t = dpi->top;
00781 const int b = dpi->top + dpi->height;
00782
00783
00784 int xl, xu, yl, yu;
00785
00786 if (dpi->width + 70 < (1 << (7 + 6))) {
00787 xl = GB(l - 70, 7, 6);
00788 xu = GB(r, 7, 6);
00789 } else {
00790
00791 xl = 0;
00792 xu = 0x3F;
00793 }
00794
00795 if (dpi->height + 70 < (1 << (6 + 6))) {
00796 yl = GB(t - 70, 6, 6) << 6;
00797 yu = GB(b, 6, 6) << 6;
00798 } else {
00799
00800 yl = 0;
00801 yu = 0x3F << 6;
00802 }
00803
00804 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00805 for (int x = xl;; x = (x + 1) & 0x3F) {
00806 const Vehicle *v = _vehicle_position_hash[x + y];
00807
00808 while (v != NULL) {
00809 if (!(v->vehstatus & VS_HIDDEN) &&
00810 l <= v->coord.right &&
00811 t <= v->coord.bottom &&
00812 r >= v->coord.left &&
00813 b >= v->coord.top) {
00814 DoDrawVehicle(v);
00815 }
00816 v = v->next_hash;
00817 }
00818
00819 if (x == xu) break;
00820 }
00821
00822 if (y == yu) break;
00823 }
00824 }
00825
00826 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00827 {
00828 Vehicle *found = NULL, *v;
00829 uint dist, best_dist = UINT_MAX;
00830
00831 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00832
00833 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00834 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00835
00836 FOR_ALL_VEHICLES(v) {
00837 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00838 x >= v->coord.left && x <= v->coord.right &&
00839 y >= v->coord.top && y <= v->coord.bottom) {
00840
00841 dist = max(
00842 abs(((v->coord.left + v->coord.right) >> 1) - x),
00843 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00844 );
00845
00846 if (dist < best_dist) {
00847 found = v;
00848 best_dist = dist;
00849 }
00850 }
00851 }
00852
00853 return found;
00854 }
00855
00856 void CheckVehicle32Day(Vehicle *v)
00857 {
00858 if ((v->day_counter & 0x1F) != 0) return;
00859
00860 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00861 if (callback == CALLBACK_FAILED) return;
00862 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00863 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00864 }
00865
00866 void DecreaseVehicleValue(Vehicle *v)
00867 {
00868 v->value -= v->value >> 8;
00869 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00870 }
00871
00872 static const byte _breakdown_chance[64] = {
00873 3, 3, 3, 3, 3, 3, 3, 3,
00874 4, 4, 5, 5, 6, 6, 7, 7,
00875 8, 8, 9, 9, 10, 10, 11, 11,
00876 12, 13, 13, 13, 13, 14, 15, 16,
00877 17, 19, 21, 25, 28, 31, 34, 37,
00878 40, 44, 48, 52, 56, 60, 64, 68,
00879 72, 80, 90, 100, 110, 120, 130, 140,
00880 150, 170, 190, 210, 230, 250, 250, 250,
00881 };
00882
00883 void CheckVehicleBreakdown(Vehicle *v)
00884 {
00885 int rel, rel_old;
00886
00887
00888 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
00889 if ((rel_old >> 8) != (rel >> 8)) InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00890
00891 if (v->breakdown_ctr != 0 || v->vehstatus & VS_STOPPED ||
00892 _settings_game.difficulty.vehicle_breakdowns < 1 ||
00893 v->cur_speed < 5 || _game_mode == GM_MENU) {
00894 return;
00895 }
00896
00897 uint32 r = Random();
00898
00899
00900 int chance = v->breakdown_chance + 1;
00901 if (Chance16I(1, 25, r)) chance += 25;
00902 v->breakdown_chance = min(255, chance);
00903
00904
00905 rel = v->reliability;
00906 if (v->type == VEH_SHIP) rel += 0x6666;
00907
00908
00909 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
00910
00911
00912 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
00913 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
00914 v->breakdown_delay = GB(r, 24, 7) + 0x80;
00915 v->breakdown_chance = 0;
00916 }
00917 }
00918
00919 void AgeVehicle(Vehicle *v)
00920 {
00921 if (v->age < 65535) v->age++;
00922
00923 int age = v->age - v->max_age;
00924 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
00925 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
00926 v->reliability_spd_dec <<= 1;
00927 }
00928
00929 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00930
00931
00932 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
00933
00934
00935 if (GetCompany(v->owner)->engine_renew && GetEngine(v->engine_type)->company_avail != 0) return;
00936
00937 StringID str;
00938 if (age == -DAYS_IN_LEAP_YEAR) {
00939 str = STR_01A0_IS_GETTING_OLD;
00940 } else if (age == 0) {
00941 str = STR_01A1_IS_GETTING_VERY_OLD;
00942 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
00943 str = STR_01A2_IS_GETTING_VERY_OLD_AND;
00944 } else {
00945 return;
00946 }
00947
00948 SetDParam(0, v->index);
00949 AddNewsItem(str, NS_ADVICE, v->index, 0);
00950 }
00951
00958 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
00959 {
00960 int count = 0;
00961 int max = 0;
00962 int cars = 0;
00963 int unloading = 0;
00964 bool loading = false;
00965
00966 const Vehicle *u = v;
00967 const Station *st = v->last_station_visited != INVALID_STATION ? GetStation(v->last_station_visited) : NULL;
00968
00969
00970 for (; v != NULL; v = v->Next()) {
00971 count += v->cargo.Count();
00972 max += v->cargo_cap;
00973 if (v->cargo_cap != 0 && colour != NULL) {
00974 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
00975 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
00976 cars++;
00977 }
00978 }
00979
00980 if (colour != NULL) {
00981 if (unloading == 0 && loading) {
00982 *colour = STR_PERCENT_UP;
00983 } else if (cars == unloading || !loading) {
00984 *colour = STR_PERCENT_DOWN;
00985 } else {
00986 *colour = STR_PERCENT_UP_DOWN;
00987 }
00988 }
00989
00990
00991 if (max == 0) return 100;
00992
00993
00994 return (count * 100) / max;
00995 }
00996
00997 void VehicleEnterDepot(Vehicle *v)
00998 {
00999 switch (v->type) {
01000 case VEH_TRAIN:
01001 InvalidateWindowClasses(WC_TRAINS_LIST);
01002
01003 SetDepotWaypointReservation(v->tile, false);
01004 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
01005
01006 if (!IsFrontEngine(v)) v = v->First();
01007 UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
01008 v->load_unload_time_rem = 0;
01009 ClrBit(v->u.rail.flags, VRF_TOGGLE_REVERSE);
01010 TrainConsistChanged(v, true);
01011 break;
01012
01013 case VEH_ROAD:
01014 InvalidateWindowClasses(WC_ROADVEH_LIST);
01015 if (!IsRoadVehFront(v)) v = v->First();
01016 break;
01017
01018 case VEH_SHIP:
01019 InvalidateWindowClasses(WC_SHIPS_LIST);
01020 v->u.ship.state = TRACK_BIT_DEPOT;
01021 RecalcShipStuff(v);
01022 break;
01023
01024 case VEH_AIRCRAFT:
01025 InvalidateWindowClasses(WC_AIRCRAFT_LIST);
01026 HandleAircraftEnterHangar(v);
01027 break;
01028 default: NOT_REACHED();
01029 }
01030
01031 if (v->type != VEH_TRAIN) {
01032
01033
01034 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01035 }
01036 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
01037
01038 v->vehstatus |= VS_HIDDEN;
01039 v->cur_speed = 0;
01040
01041 VehicleServiceInDepot(v);
01042
01043 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01044
01045 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01046 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01047
01048 const Order *real_order = GetVehicleOrder(v, v->cur_order_index);
01049 Order t = v->current_order;
01050 v->current_order.MakeDummy();
01051
01052
01053
01054 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01055 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01056 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01057
01058 return;
01059 }
01060
01061 if (t.IsRefit()) {
01062 _current_company = v->owner;
01063 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01064
01065 if (CmdFailed(cost)) {
01066 _vehicles_to_autoreplace[v] = false;
01067 if (v->owner == _local_company) {
01068
01069 SetDParam(0, v->index);
01070 AddNewsItem(STR_ORDER_REFIT_FAILED, NS_ADVICE, v->index, 0);
01071 }
01072 } else if (v->owner == _local_company && cost.GetCost() != 0) {
01073 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01074 }
01075 }
01076
01077 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01078
01079 UpdateVehicleTimetable(v, true);
01080 v->cur_order_index++;
01081 }
01082 if (t.GetDepotActionType() & ODATFB_HALT) {
01083
01084 _vehicles_to_autoreplace[v] = false;
01085 if (v->owner == _local_company) {
01086 StringID string;
01087
01088 switch (v->type) {
01089 case VEH_TRAIN: string = STR_8814_TRAIN_IS_WAITING_IN_DEPOT; break;
01090 case VEH_ROAD: string = STR_9016_ROAD_VEHICLE_IS_WAITING; break;
01091 case VEH_SHIP: string = STR_981C_SHIP_IS_WAITING_IN_DEPOT; break;
01092 case VEH_AIRCRAFT: string = STR_A014_AIRCRAFT_IS_WAITING_IN; break;
01093 default: NOT_REACHED(); string = STR_EMPTY;
01094 }
01095
01096 SetDParam(0, v->index);
01097 AddNewsItem(string, NS_ADVICE, v->index, 0);
01098 }
01099 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01100 }
01101 }
01102 }
01103
01104
01112 void VehicleMove(Vehicle *v, bool update_viewport)
01113 {
01114 int img = v->cur_image;
01115 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01116 const Sprite *spr = GetSprite(img, ST_NORMAL);
01117
01118 pt.x += spr->x_offs;
01119 pt.y += spr->y_offs;
01120
01121 UpdateVehiclePosHash(v, pt.x, pt.y);
01122
01123 Rect old_coord = v->coord;
01124 v->coord.left = pt.x;
01125 v->coord.top = pt.y;
01126 v->coord.right = pt.x + spr->width + 2;
01127 v->coord.bottom = pt.y + spr->height + 2;
01128
01129 if (update_viewport) {
01130 MarkAllViewportsDirty(
01131 min(old_coord.left, v->coord.left),
01132 min(old_coord.top, v->coord.top),
01133 max(old_coord.right, v->coord.right) + 1,
01134 max(old_coord.bottom, v->coord.bottom) + 1
01135 );
01136 }
01137 }
01138
01147 void MarkSingleVehicleDirty(const Vehicle *v)
01148 {
01149 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01150 }
01151
01156 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01157 {
01158 static const int8 _delta_coord[16] = {
01159 -1,-1,-1, 0, 1, 1, 1, 0,
01160 -1, 0, 1, 1, 1, 0,-1,-1,
01161 };
01162
01163 int x = v->x_pos + _delta_coord[v->direction];
01164 int y = v->y_pos + _delta_coord[v->direction + 8];
01165
01166 GetNewVehiclePosResult gp;
01167 gp.x = x;
01168 gp.y = y;
01169 gp.old_tile = v->tile;
01170 gp.new_tile = TileVirtXY(x, y);
01171 return gp;
01172 }
01173
01174 static const Direction _new_direction_table[] = {
01175 DIR_N , DIR_NW, DIR_W ,
01176 DIR_NE, DIR_SE, DIR_SW,
01177 DIR_E , DIR_SE, DIR_S
01178 };
01179
01180 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01181 {
01182 int i = 0;
01183
01184 if (y >= v->y_pos) {
01185 if (y != v->y_pos) i += 3;
01186 i += 3;
01187 }
01188
01189 if (x >= v->x_pos) {
01190 if (x != v->x_pos) i++;
01191 i++;
01192 }
01193
01194 Direction dir = v->direction;
01195
01196 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01197 if (dirdiff == DIRDIFF_SAME) return dir;
01198 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01199 }
01200
01201 Trackdir GetVehicleTrackdir(const Vehicle *v)
01202 {
01203 if (v->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
01204
01205 switch (v->type) {
01206 case VEH_TRAIN:
01207 if (v->u.rail.track == TRACK_BIT_DEPOT)
01208 return DiagDirToDiagTrackdir(GetRailDepotDirection(v->tile));
01209
01210 if (v->u.rail.track == TRACK_BIT_WORMHOLE)
01211 return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01212
01213 return TrackDirectionToTrackdir(FindFirstTrack(v->u.rail.track), v->direction);
01214
01215 case VEH_SHIP:
01216 if (v->IsInDepot())
01217
01218 return DiagDirToDiagTrackdir(GetShipDepotDirection(v->tile));
01219
01220 if (v->u.ship.state == TRACK_BIT_WORMHOLE)
01221 return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01222
01223 return TrackDirectionToTrackdir(FindFirstTrack(v->u.ship.state), v->direction);
01224
01225 case VEH_ROAD:
01226 if (v->IsInDepot())
01227 return DiagDirToDiagTrackdir(GetRoadDepotDirection(v->tile));
01228
01229 if (IsStandardRoadStopTile(v->tile))
01230 return DiagDirToDiagTrackdir(GetRoadStopDir(v->tile));
01231
01232
01233 if (v->u.road.state > RVSB_TRACKDIR_MASK) return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01234
01235
01236
01237 return (Trackdir)((IsReversingRoadTrackdir((Trackdir)v->u.road.state)) ? (v->u.road.state - 6) : v->u.road.state);
01238
01239
01240 default: return INVALID_TRACKDIR;
01241 }
01242 }
01243
01253 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01254 {
01255 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01256 }
01257
01258 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01259 {
01260
01261 const Vehicle *v;
01262 FOR_ALL_VEHICLES(v) {
01263 if (v->type == type && v->owner == owner) {
01264 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01265 }
01266 }
01267
01268 if (this->maxid == 0) return;
01269
01270 this->maxid++;
01271 this->maxid++;
01272
01273 this->cache = CallocT<bool>(this->maxid);
01274
01275
01276 FOR_ALL_VEHICLES(v) {
01277 if (v->type == type && v->owner == owner) {
01278 this->cache[v->unitnumber] = true;
01279 }
01280 }
01281 }
01282
01283 UnitID FreeUnitIDGenerator::NextID()
01284 {
01285 if (this->maxid <= this->curid) return ++this->curid;
01286
01287 while (this->cache[++this->curid]) { }
01288
01289 return this->curid;
01290 }
01291
01292 UnitID GetFreeUnitNumber(VehicleType type)
01293 {
01294 FreeUnitIDGenerator gen(type, _current_company);
01295
01296 return gen.NextID();
01297 }
01298
01299
01308 bool CanBuildVehicleInfrastructure(VehicleType type)
01309 {
01310 assert(IsCompanyBuildableVehicleType(type));
01311
01312 if (!IsValidCompanyID(_local_company)) return false;
01313 if (_settings_client.gui.always_build_infrastructure) return true;
01314
01315 UnitID max;
01316 switch (type) {
01317 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01318 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01319 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01320 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01321 default: NOT_REACHED();
01322 }
01323
01324
01325 if (max > 0) {
01326
01327 const Engine *e;
01328 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01329 if (HasBit(e->company_avail, _local_company)) return true;
01330 }
01331 return false;
01332 }
01333
01334
01335 const Vehicle *v;
01336 FOR_ALL_VEHICLES(v) {
01337 if (v->owner == _local_company && v->type == type) return true;
01338 }
01339
01340 return false;
01341 }
01342
01343
01344 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01345 {
01346 const Company *c = GetCompany(company);
01347 LiveryScheme scheme = LS_DEFAULT;
01348 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01349
01350
01351
01352 if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
01353
01354 const Engine *e = GetEngine(engine_type);
01355 switch (e->type) {
01356 default: NOT_REACHED();
01357 case VEH_TRAIN: {
01358 const RailVehicleInfo *rvi = RailVehInfo(engine_type);
01359 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (IsArticulatedPart(v) && rvi->railveh_type != RAILVEH_WAGON))) {
01360
01361
01362 engine_type = parent_engine_type;
01363 e = GetEngine(engine_type);
01364 rvi = RailVehInfo(engine_type);
01365
01366 }
01367
01368 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01369 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01370 if (rvi->railveh_type == RAILVEH_WAGON) {
01371 if (!GetCargo(cargo_type)->is_freight) {
01372 if (parent_engine_type == INVALID_ENGINE) {
01373 scheme = LS_PASSENGER_WAGON_STEAM;
01374 } else {
01375 switch (RailVehInfo(parent_engine_type)->engclass) {
01376 default: NOT_REACHED();
01377 case EC_STEAM: scheme = LS_PASSENGER_WAGON_STEAM; break;
01378 case EC_DIESEL: scheme = LS_PASSENGER_WAGON_DIESEL; break;
01379 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
01380 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
01381 case EC_MAGLEV: scheme = LS_PASSENGER_WAGON_MAGLEV; break;
01382 }
01383 }
01384 } else {
01385 scheme = LS_FREIGHT_WAGON;
01386 }
01387 } else {
01388 bool is_mu = HasBit(EngInfo(engine_type)->misc_flags, EF_RAIL_IS_MU);
01389
01390 switch (rvi->engclass) {
01391 default: NOT_REACHED();
01392 case EC_STEAM: scheme = LS_STEAM; break;
01393 case EC_DIESEL: scheme = is_mu ? LS_DMU : LS_DIESEL; break;
01394 case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
01395 case EC_MONORAIL: scheme = LS_MONORAIL; break;
01396 case EC_MAGLEV: scheme = LS_MAGLEV; break;
01397 }
01398 }
01399 break;
01400 }
01401
01402 case VEH_ROAD: {
01403
01404 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01405 engine_type = parent_engine_type;
01406 e = GetEngine(engine_type);
01407 cargo_type = v->First()->cargo_type;
01408 }
01409 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01410 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01411
01412
01413 if (HasBit(EngInfo(engine_type)->misc_flags, EF_ROAD_TRAM)) {
01414
01415 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01416 } else {
01417
01418 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01419 }
01420 break;
01421 }
01422
01423 case VEH_SHIP: {
01424 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01425 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01426 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01427 break;
01428 }
01429
01430 case VEH_AIRCRAFT: {
01431 switch (e->u.air.subtype) {
01432 case AIR_HELI: scheme = LS_HELICOPTER; break;
01433 case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
01434 case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
01435 }
01436 break;
01437 }
01438 }
01439
01440
01441 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01442 }
01443
01444 return &c->livery[scheme];
01445 }
01446
01447
01448 static SpriteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01449 {
01450 SpriteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01451
01452
01453 if (map != PAL_NONE) return map;
01454
01455
01456 if (HasBit(EngInfo(engine_type)->callbackmask, CBM_VEHICLE_COLOUR_REMAP)) {
01457 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01458
01459
01460 if (callback != CALLBACK_FAILED && callback != 0xC000) {
01461 map = GB(callback, 0, 14);
01462
01463
01464 if (!HasBit(callback, 14)) {
01465
01466 if (v != NULL) ((Vehicle*)v)->colourmap = map;
01467 return map;
01468 }
01469 }
01470 }
01471
01472 bool twocc = HasBit(EngInfo(engine_type)->misc_flags, EF_USES_2CC);
01473
01474 if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOUR_START;
01475
01476 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
01477
01478 map += livery->colour1;
01479 if (twocc) map += livery->colour2 * 16;
01480
01481
01482 if (v != NULL) ((Vehicle*)v)->colourmap = map;
01483 return map;
01484 }
01485
01486 SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
01487 {
01488 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01489 }
01490
01491 SpriteID GetVehiclePalette(const Vehicle *v)
01492 {
01493 if (v->type == VEH_TRAIN) {
01494 return GetEngineColourMap(v->engine_type, v->owner, v->u.rail.first_engine, v);
01495 } else if (v->type == VEH_ROAD) {
01496 return GetEngineColourMap(v->engine_type, v->owner, v->u.road.first_engine, v);
01497 }
01498
01499 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01500 }
01501
01502
01503 void Vehicle::BeginLoading()
01504 {
01505 assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01506
01507 if (this->current_order.IsType(OT_GOTO_STATION) &&
01508 this->current_order.GetDestination() == this->last_station_visited) {
01509 current_order.MakeLoading(true);
01510 UpdateVehicleTimetable(this, true);
01511
01512
01513
01514
01515
01516
01517 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01518
01519 } else {
01520 current_order.MakeLoading(false);
01521 }
01522
01523 GetStation(this->last_station_visited)->loading_vehicles.push_back(this);
01524
01525 PrepareUnload(this);
01526
01527 InvalidateWindow(GetWindowClassForVehicleType(this->type), this->owner);
01528 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01529 InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
01530 InvalidateWindow(WC_STATION_VIEW, this->last_station_visited);
01531
01532 GetStation(this->last_station_visited)->MarkTilesDirty(true);
01533 this->cur_speed = 0;
01534 this->MarkDirty();
01535 }
01536
01537 void Vehicle::LeaveStation()
01538 {
01539 assert(current_order.IsType(OT_LOADING));
01540
01541 delete this->cargo_payment;
01542
01543
01544 if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01545
01546 current_order.MakeLeaveStation();
01547 Station *st = GetStation(this->last_station_visited);
01548 st->loading_vehicles.remove(this);
01549
01550 HideFillingPercent(&this->fill_percent_te_id);
01551
01552 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01553
01554 if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
01555
01556
01557
01558
01559 if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(GetVehicleTrackdir(this)), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
01560 TryPathReserve(this, true, true);
01561 }
01562 }
01563 }
01564
01565
01566 void Vehicle::HandleLoading(bool mode)
01567 {
01568 switch (this->current_order.GetType()) {
01569 case OT_LOADING: {
01570 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01571
01572
01573 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01574 (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01575
01576 this->PlayLeaveStationSound();
01577
01578 bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
01579 this->LeaveStation();
01580
01581
01582 if (!at_destination_station) return;
01583 break;
01584 }
01585
01586 case OT_DUMMY: break;
01587
01588 default: return;
01589 }
01590
01591 this->cur_order_index++;
01592 InvalidateVehicleOrder(this, 0);
01593 }
01594
01595 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01596 {
01597 if (!CheckOwnership(this->owner)) return CMD_ERROR;
01598 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01599 if (this->IsStoppedInDepot()) return CMD_ERROR;
01600
01601 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01602 bool halt_in_depot = this->current_order.GetDepotActionType() & ODATFB_HALT;
01603 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01604
01605
01606
01607 if (flags & DC_EXEC) {
01608 this->current_order.SetDepotOrderType(ODTF_MANUAL);
01609 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01610 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01611 }
01612 return CommandCost();
01613 }
01614
01615 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
01616 if (flags & DC_EXEC) {
01617
01618
01619 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->cur_order_index++;
01620
01621 this->current_order.MakeDummy();
01622 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01623 }
01624 return CommandCost();
01625 }
01626
01627 TileIndex location;
01628 DestinationID destination;
01629 bool reverse;
01630 static const StringID no_depot[] = {STR_883A_UNABLE_TO_FIND_ROUTE_TO, STR_9019_UNABLE_TO_FIND_LOCAL_DEPOT, STR_981A_UNABLE_TO_FIND_LOCAL_DEPOT, STR_A012_CAN_T_SEND_AIRCRAFT_TO};
01631 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01632
01633 if (flags & DC_EXEC) {
01634 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01635
01636 this->dest_tile = location;
01637 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01638 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01639 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01640
01641
01642 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01643
01644 if (this->type == VEH_AIRCRAFT && this->u.air.state == FLYING && this->u.air.targetairport != destination) {
01645
01646 extern void AircraftNextAirportPos_and_Order(Vehicle *v);
01647 AircraftNextAirportPos_and_Order(this);
01648 }
01649 }
01650
01651 return CommandCost();
01652
01653 }
01654
01655 void Vehicle::SetNext(Vehicle *next)
01656 {
01657 if (this->next != NULL) {
01658
01659 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01660 v->first = this->next;
01661 }
01662 this->next->previous = NULL;
01663 }
01664
01665 this->next = next;
01666
01667 if (this->next != NULL) {
01668
01669 if (this->next->previous != NULL) this->next->previous->next = NULL;
01670 this->next->previous = this;
01671 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01672 v->first = this->first;
01673 }
01674 }
01675 }
01676
01677 void Vehicle::AddToShared(Vehicle *shared_chain)
01678 {
01679 assert(this->previous_shared == NULL && this->next_shared == NULL);
01680
01681 if (!shared_chain->orders.list) {
01682 assert(shared_chain->previous_shared == NULL);
01683 assert(shared_chain->next_shared == NULL);
01684 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
01685 }
01686
01687 this->next_shared = shared_chain->next_shared;
01688 this->previous_shared = shared_chain;
01689
01690 shared_chain->next_shared = this;
01691
01692 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
01693
01694 shared_chain->orders.list->AddVehicle(this);
01695 }
01696
01697 void Vehicle::RemoveFromShared()
01698 {
01699
01700
01701 bool were_first = (this->FirstShared() == this);
01702 uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
01703
01704 this->orders.list->RemoveVehicle(this);
01705
01706 if (!were_first) {
01707
01708 this->previous_shared->next_shared = this->NextShared();
01709 }
01710
01711 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
01712
01713
01714 if (this->orders.list->GetNumVehicles() == 1) {
01715
01716 DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
01717 InvalidateVehicleOrder(this->FirstShared(), 0);
01718 } else if (were_first) {
01719
01720
01721 InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
01722 }
01723
01724 this->next_shared = NULL;
01725 this->previous_shared = NULL;
01726 }
01727
01728 void StopAllVehicles()
01729 {
01730 Vehicle *v;
01731 FOR_ALL_VEHICLES(v) {
01732
01733
01734 v->vehstatus |= VS_STOPPED;
01735 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01736 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
01737 }
01738 }
01739
01740 void VehiclesYearlyLoop()
01741 {
01742 Vehicle *v;
01743 FOR_ALL_VEHICLES(v) {
01744 if (v->IsPrimaryVehicle()) {
01745
01746 Money profit = v->GetDisplayProfitThisYear();
01747 if (v->age >= 730 && profit < 0) {
01748 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
01749 SetDParam(0, v->index);
01750 SetDParam(1, profit);
01751 AddNewsItem(
01752 STR_VEHICLE_IS_UNPROFITABLE,
01753 NS_ADVICE,
01754 v->index,
01755 0);
01756 }
01757 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
01758 }
01759
01760 v->profit_last_year = v->profit_this_year;
01761 v->profit_this_year = 0;
01762 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01763 }
01764 }
01765 }
01766
01767
01777 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
01778 {
01779 assert(IsEngineIndex(engine_type));
01780 const Engine *e = GetEngine(engine_type);
01781
01782 switch (e->type) {
01783 case VEH_TRAIN:
01784 return (st->facilities & FACIL_TRAIN) != 0;
01785
01786 case VEH_ROAD:
01787
01788
01789
01790 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
01791
01792 case VEH_SHIP:
01793 return (st->facilities & FACIL_DOCK) != 0;
01794
01795 case VEH_AIRCRAFT:
01796 return (st->facilities & FACIL_AIRPORT) != 0 &&
01797 (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
01798
01799 default:
01800 return false;
01801 }
01802 }
01803
01810 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
01811 {
01812 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(v) != NULL;
01813
01814 return CanVehicleUseStation(v->engine_type, st);
01815 }