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