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 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00605
00606
00607
00608
00609
00610
00611 v->vehstatus |= VS_STOPPED;
00612 }
00613
00614 void CallVehicleTicks()
00615 {
00616 _vehicles_to_autoreplace.Clear();
00617
00618 Station *st;
00619 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00620
00621 Vehicle *v;
00622 FOR_ALL_VEHICLES(v) {
00623 v->Tick();
00624
00625 switch (v->type) {
00626 default: break;
00627
00628 case VEH_TRAIN:
00629 case VEH_ROAD:
00630 case VEH_AIRCRAFT:
00631 case VEH_SHIP:
00632 if (v->type == VEH_TRAIN && IsTrainWagon(v)) continue;
00633 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00634 if (v->type == VEH_ROAD && !IsRoadVehFront(v)) continue;
00635
00636 v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed;
00637
00638 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00639
00640
00641 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00642 }
00643 }
00644
00645 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00646 v = it->first;
00647
00648 _current_company = v->owner;
00649
00650
00651
00652
00653 if (it->second) v->vehstatus &= ~VS_STOPPED;
00654
00655
00656 int x = v->x_pos;
00657 int y = v->y_pos;
00658 int z = v->z_pos;
00659
00660 const Company *c = GetCompany(_current_company);
00661 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->engine_renew_money));
00662 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00663 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->engine_renew_money));
00664
00665 if (!IsLocalCompany()) continue;
00666
00667 if (res.Succeeded()) {
00668 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00669 continue;
00670 }
00671
00672 StringID error_message = res.GetErrorMessage();
00673 if (error_message == STR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00674
00675 if (error_message == STR_0003_NOT_ENOUGH_CASH_REQUIRES) error_message = STR_AUTOREPLACE_MONEY_LIMIT;
00676
00677 StringID message;
00678 if (error_message == STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00679 message = error_message;
00680 } else {
00681 message = STR_VEHICLE_AUTORENEW_FAILED;
00682 }
00683
00684 SetDParam(0, v->index);
00685 SetDParam(1, error_message);
00686 AddNewsItem(message, NS_ADVICE, v->index, 0);
00687 }
00688
00689 _current_company = OWNER_NONE;
00690 }
00691
00697 bool CanRefitTo(EngineID engine_type, CargoID cid_to)
00698 {
00699 return HasBit(EngInfo(engine_type)->refit_mask, cid_to);
00700 }
00701
00706 CargoID FindFirstRefittableCargo(EngineID engine_type)
00707 {
00708 uint32 refit_mask = EngInfo(engine_type)->refit_mask;
00709
00710 if (refit_mask != 0) {
00711 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00712 if (HasBit(refit_mask, cid)) return cid;
00713 }
00714 }
00715
00716 return CT_INVALID;
00717 }
00718
00723 CommandCost GetRefitCost(EngineID engine_type)
00724 {
00725 Money base_cost;
00726 ExpensesType expense_type;
00727 switch (GetEngine(engine_type)->type) {
00728 case VEH_SHIP:
00729 base_cost = _price.ship_base;
00730 expense_type = EXPENSES_SHIP_RUN;
00731 break;
00732
00733 case VEH_ROAD:
00734 base_cost = _price.roadveh_base;
00735 expense_type = EXPENSES_ROADVEH_RUN;
00736 break;
00737
00738 case VEH_AIRCRAFT:
00739 base_cost = _price.aircraft_base;
00740 expense_type = EXPENSES_AIRCRAFT_RUN;
00741 break;
00742
00743 case VEH_TRAIN:
00744 base_cost = 2 * ((RailVehInfo(engine_type)->railveh_type == RAILVEH_WAGON) ?
00745 _price.build_railwagon : _price.build_railvehicle);
00746 expense_type = EXPENSES_TRAIN_RUN;
00747 break;
00748
00749 default: NOT_REACHED();
00750 }
00751 return CommandCost(expense_type, (EngInfo(engine_type)->refit_cost * base_cost) >> 10);
00752 }
00753
00754 static void DoDrawVehicle(const Vehicle *v)
00755 {
00756 SpriteID image = v->cur_image;
00757 SpriteID pal = PAL_NONE;
00758
00759 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00760
00761 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00762 v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00763 }
00764
00765 void ViewportAddVehicles(DrawPixelInfo *dpi)
00766 {
00767
00768 const int l = dpi->left;
00769 const int r = dpi->left + dpi->width;
00770 const int t = dpi->top;
00771 const int b = dpi->top + dpi->height;
00772
00773
00774 int xl, xu, yl, yu;
00775
00776 if (dpi->width + 70 < (1 << (7 + 6))) {
00777 xl = GB(l - 70, 7, 6);
00778 xu = GB(r, 7, 6);
00779 } else {
00780
00781 xl = 0;
00782 xu = 0x3F;
00783 }
00784
00785 if (dpi->height + 70 < (1 << (6 + 6))) {
00786 yl = GB(t - 70, 6, 6) << 6;
00787 yu = GB(b, 6, 6) << 6;
00788 } else {
00789
00790 yl = 0;
00791 yu = 0x3F << 6;
00792 }
00793
00794 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00795 for (int x = xl;; x = (x + 1) & 0x3F) {
00796 const Vehicle *v = _vehicle_position_hash[x + y];
00797
00798 while (v != NULL) {
00799 if (!(v->vehstatus & VS_HIDDEN) &&
00800 l <= v->coord.right &&
00801 t <= v->coord.bottom &&
00802 r >= v->coord.left &&
00803 b >= v->coord.top) {
00804 DoDrawVehicle(v);
00805 }
00806 v = v->next_hash;
00807 }
00808
00809 if (x == xu) break;
00810 }
00811
00812 if (y == yu) break;
00813 }
00814 }
00815
00816 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00817 {
00818 Vehicle *found = NULL, *v;
00819 uint dist, best_dist = UINT_MAX;
00820
00821 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00822
00823 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00824 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00825
00826 FOR_ALL_VEHICLES(v) {
00827 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00828 x >= v->coord.left && x <= v->coord.right &&
00829 y >= v->coord.top && y <= v->coord.bottom) {
00830
00831 dist = max(
00832 abs(((v->coord.left + v->coord.right) >> 1) - x),
00833 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00834 );
00835
00836 if (dist < best_dist) {
00837 found = v;
00838 best_dist = dist;
00839 }
00840 }
00841 }
00842
00843 return found;
00844 }
00845
00846 void CheckVehicle32Day(Vehicle *v)
00847 {
00848 if ((v->day_counter & 0x1F) != 0) return;
00849
00850 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00851 if (callback == CALLBACK_FAILED) return;
00852 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00853 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00854 }
00855
00856 void DecreaseVehicleValue(Vehicle *v)
00857 {
00858 v->value -= v->value >> 8;
00859 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00860 }
00861
00862 static const byte _breakdown_chance[64] = {
00863 3, 3, 3, 3, 3, 3, 3, 3,
00864 4, 4, 5, 5, 6, 6, 7, 7,
00865 8, 8, 9, 9, 10, 10, 11, 11,
00866 12, 13, 13, 13, 13, 14, 15, 16,
00867 17, 19, 21, 25, 28, 31, 34, 37,
00868 40, 44, 48, 52, 56, 60, 64, 68,
00869 72, 80, 90, 100, 110, 120, 130, 140,
00870 150, 170, 190, 210, 230, 250, 250, 250,
00871 };
00872
00873 void CheckVehicleBreakdown(Vehicle *v)
00874 {
00875 int rel, rel_old;
00876
00877
00878 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
00879 if ((rel_old >> 8) != (rel >> 8)) InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00880
00881 if (v->breakdown_ctr != 0 || v->vehstatus & VS_STOPPED ||
00882 _settings_game.difficulty.vehicle_breakdowns < 1 ||
00883 v->cur_speed < 5 || _game_mode == GM_MENU) {
00884 return;
00885 }
00886
00887 uint32 r = Random();
00888
00889
00890 int chance = v->breakdown_chance + 1;
00891 if (Chance16I(1, 25, r)) chance += 25;
00892 v->breakdown_chance = min(255, chance);
00893
00894
00895 rel = v->reliability;
00896 if (v->type == VEH_SHIP) rel += 0x6666;
00897
00898
00899 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
00900
00901
00902 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
00903 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
00904 v->breakdown_delay = GB(r, 24, 7) + 0x80;
00905 v->breakdown_chance = 0;
00906 }
00907 }
00908
00909 void AgeVehicle(Vehicle *v)
00910 {
00911 if (v->age < 65535) v->age++;
00912
00913 int age = v->age - v->max_age;
00914 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
00915 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
00916 v->reliability_spd_dec <<= 1;
00917 }
00918
00919 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00920
00921
00922 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
00923
00924
00925 if (GetCompany(v->owner)->engine_renew && GetEngine(v->engine_type)->company_avail != 0) return;
00926
00927 StringID str;
00928 if (age == -DAYS_IN_LEAP_YEAR) {
00929 str = STR_01A0_IS_GETTING_OLD;
00930 } else if (age == 0) {
00931 str = STR_01A1_IS_GETTING_VERY_OLD;
00932 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
00933 str = STR_01A2_IS_GETTING_VERY_OLD_AND;
00934 } else {
00935 return;
00936 }
00937
00938 SetDParam(0, v->index);
00939 AddNewsItem(str, NS_ADVICE, v->index, 0);
00940 }
00941
00948 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
00949 {
00950 int count = 0;
00951 int max = 0;
00952 int cars = 0;
00953 int unloading = 0;
00954 bool loading = false;
00955
00956 const Vehicle *u = v;
00957 const Station *st = v->last_station_visited != INVALID_STATION ? GetStation(v->last_station_visited) : NULL;
00958
00959
00960 for (; v != NULL; v = v->Next()) {
00961 count += v->cargo.Count();
00962 max += v->cargo_cap;
00963 if (v->cargo_cap != 0 && colour != NULL) {
00964 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
00965 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
00966 cars++;
00967 }
00968 }
00969
00970 if (colour != NULL) {
00971 if (unloading == 0 && loading) {
00972 *colour = STR_PERCENT_UP;
00973 } else if (cars == unloading || !loading) {
00974 *colour = STR_PERCENT_DOWN;
00975 } else {
00976 *colour = STR_PERCENT_UP_DOWN;
00977 }
00978 }
00979
00980
00981 if (max == 0) return 100;
00982
00983
00984 return (count * 100) / max;
00985 }
00986
00987 void VehicleEnterDepot(Vehicle *v)
00988 {
00989 switch (v->type) {
00990 case VEH_TRAIN:
00991 InvalidateWindowClasses(WC_TRAINS_LIST);
00992
00993 SetDepotWaypointReservation(v->tile, false);
00994 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
00995
00996 if (!IsFrontEngine(v)) v = v->First();
00997 UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
00998 v->load_unload_time_rem = 0;
00999 ClrBit(v->u.rail.flags, VRF_TOGGLE_REVERSE);
01000 TrainConsistChanged(v, true);
01001 break;
01002
01003 case VEH_ROAD:
01004 InvalidateWindowClasses(WC_ROADVEH_LIST);
01005 if (!IsRoadVehFront(v)) v = v->First();
01006 break;
01007
01008 case VEH_SHIP:
01009 InvalidateWindowClasses(WC_SHIPS_LIST);
01010 v->u.ship.state = TRACK_BIT_DEPOT;
01011 RecalcShipStuff(v);
01012 break;
01013
01014 case VEH_AIRCRAFT:
01015 InvalidateWindowClasses(WC_AIRCRAFT_LIST);
01016 HandleAircraftEnterHangar(v);
01017 break;
01018 default: NOT_REACHED();
01019 }
01020
01021 if (v->type != VEH_TRAIN) {
01022
01023
01024 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01025 }
01026 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
01027
01028 v->vehstatus |= VS_HIDDEN;
01029 v->cur_speed = 0;
01030
01031 VehicleServiceInDepot(v);
01032
01033 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01034
01035 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01036 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01037
01038 const Order *real_order = GetVehicleOrder(v, v->cur_order_index);
01039 Order t = v->current_order;
01040 v->current_order.MakeDummy();
01041
01042
01043
01044 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01045 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01046 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01047
01048 return;
01049 }
01050
01051 if (t.IsRefit()) {
01052 _current_company = v->owner;
01053 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01054
01055 if (CmdFailed(cost)) {
01056 _vehicles_to_autoreplace[v] = false;
01057 if (v->owner == _local_company) {
01058
01059 SetDParam(0, v->index);
01060 AddNewsItem(STR_ORDER_REFIT_FAILED, NS_ADVICE, v->index, 0);
01061 }
01062 } else if (v->owner == _local_company && cost.GetCost() != 0) {
01063 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01064 }
01065 }
01066
01067 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01068
01069 UpdateVehicleTimetable(v, true);
01070 v->cur_order_index++;
01071 }
01072 if (t.GetDepotActionType() & ODATFB_HALT) {
01073
01074 _vehicles_to_autoreplace[v] = false;
01075 if (v->owner == _local_company) {
01076 StringID string;
01077
01078 switch (v->type) {
01079 case VEH_TRAIN: string = STR_8814_TRAIN_IS_WAITING_IN_DEPOT; break;
01080 case VEH_ROAD: string = STR_9016_ROAD_VEHICLE_IS_WAITING; break;
01081 case VEH_SHIP: string = STR_981C_SHIP_IS_WAITING_IN_DEPOT; break;
01082 case VEH_AIRCRAFT: string = STR_A014_AIRCRAFT_IS_WAITING_IN; break;
01083 default: NOT_REACHED(); string = STR_EMPTY;
01084 }
01085
01086 SetDParam(0, v->index);
01087 AddNewsItem(string, NS_ADVICE, v->index, 0);
01088 }
01089 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01090 }
01091 }
01092 }
01093
01094
01102 void VehicleMove(Vehicle *v, bool update_viewport)
01103 {
01104 int img = v->cur_image;
01105 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01106 const Sprite *spr = GetSprite(img, ST_NORMAL);
01107
01108 pt.x += spr->x_offs;
01109 pt.y += spr->y_offs;
01110
01111 UpdateVehiclePosHash(v, pt.x, pt.y);
01112
01113 Rect old_coord = v->coord;
01114 v->coord.left = pt.x;
01115 v->coord.top = pt.y;
01116 v->coord.right = pt.x + spr->width + 2;
01117 v->coord.bottom = pt.y + spr->height + 2;
01118
01119 if (update_viewport) {
01120 MarkAllViewportsDirty(
01121 min(old_coord.left, v->coord.left),
01122 min(old_coord.top, v->coord.top),
01123 max(old_coord.right, v->coord.right) + 1,
01124 max(old_coord.bottom, v->coord.bottom) + 1
01125 );
01126 }
01127 }
01128
01137 void MarkSingleVehicleDirty(const Vehicle *v)
01138 {
01139 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01140 }
01141
01146 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01147 {
01148 static const int8 _delta_coord[16] = {
01149 -1,-1,-1, 0, 1, 1, 1, 0,
01150 -1, 0, 1, 1, 1, 0,-1,-1,
01151 };
01152
01153 int x = v->x_pos + _delta_coord[v->direction];
01154 int y = v->y_pos + _delta_coord[v->direction + 8];
01155
01156 GetNewVehiclePosResult gp;
01157 gp.x = x;
01158 gp.y = y;
01159 gp.old_tile = v->tile;
01160 gp.new_tile = TileVirtXY(x, y);
01161 return gp;
01162 }
01163
01164 static const Direction _new_direction_table[] = {
01165 DIR_N , DIR_NW, DIR_W ,
01166 DIR_NE, DIR_SE, DIR_SW,
01167 DIR_E , DIR_SE, DIR_S
01168 };
01169
01170 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01171 {
01172 int i = 0;
01173
01174 if (y >= v->y_pos) {
01175 if (y != v->y_pos) i += 3;
01176 i += 3;
01177 }
01178
01179 if (x >= v->x_pos) {
01180 if (x != v->x_pos) i++;
01181 i++;
01182 }
01183
01184 Direction dir = v->direction;
01185
01186 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01187 if (dirdiff == DIRDIFF_SAME) return dir;
01188 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01189 }
01190
01191 Trackdir GetVehicleTrackdir(const Vehicle *v)
01192 {
01193 if (v->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
01194
01195 switch (v->type) {
01196 case VEH_TRAIN:
01197 if (v->u.rail.track == TRACK_BIT_DEPOT)
01198 return DiagDirToDiagTrackdir(GetRailDepotDirection(v->tile));
01199
01200 if (v->u.rail.track == TRACK_BIT_WORMHOLE)
01201 return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01202
01203 return TrackDirectionToTrackdir(FindFirstTrack(v->u.rail.track), v->direction);
01204
01205 case VEH_SHIP:
01206 if (v->IsInDepot())
01207
01208 return DiagDirToDiagTrackdir(GetShipDepotDirection(v->tile));
01209
01210 if (v->u.ship.state == TRACK_BIT_WORMHOLE)
01211 return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01212
01213 return TrackDirectionToTrackdir(FindFirstTrack(v->u.ship.state), v->direction);
01214
01215 case VEH_ROAD:
01216 if (v->IsInDepot())
01217 return DiagDirToDiagTrackdir(GetRoadDepotDirection(v->tile));
01218
01219 if (IsStandardRoadStopTile(v->tile))
01220 return DiagDirToDiagTrackdir(GetRoadStopDir(v->tile));
01221
01222
01223 if (v->u.road.state > RVSB_TRACKDIR_MASK) return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01224
01225
01226
01227 return (Trackdir)((IsReversingRoadTrackdir((Trackdir)v->u.road.state)) ? (v->u.road.state - 6) : v->u.road.state);
01228
01229
01230 default: return INVALID_TRACKDIR;
01231 }
01232 }
01233
01243 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01244 {
01245 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01246 }
01247
01248 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01249 {
01250
01251 const Vehicle *v;
01252 FOR_ALL_VEHICLES(v) {
01253 if (v->type == type && v->owner == owner) {
01254 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01255 }
01256 }
01257
01258 if (this->maxid == 0) return;
01259
01260 this->maxid++;
01261 this->maxid++;
01262
01263 this->cache = CallocT<bool>(this->maxid);
01264
01265
01266 FOR_ALL_VEHICLES(v) {
01267 if (v->type == type && v->owner == owner) {
01268 this->cache[v->unitnumber] = true;
01269 }
01270 }
01271 }
01272
01273 UnitID FreeUnitIDGenerator::NextID()
01274 {
01275 if (this->maxid <= this->curid) return ++this->curid;
01276
01277 while (this->cache[++this->curid]) { }
01278
01279 return this->curid;
01280 }
01281
01282 UnitID GetFreeUnitNumber(VehicleType type)
01283 {
01284 FreeUnitIDGenerator gen(type, _current_company);
01285
01286 return gen.NextID();
01287 }
01288
01289
01298 bool CanBuildVehicleInfrastructure(VehicleType type)
01299 {
01300 assert(IsCompanyBuildableVehicleType(type));
01301
01302 if (!IsValidCompanyID(_local_company)) return false;
01303 if (_settings_client.gui.always_build_infrastructure) return true;
01304
01305 UnitID max;
01306 switch (type) {
01307 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01308 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01309 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01310 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01311 default: NOT_REACHED();
01312 }
01313
01314
01315 if (max > 0) {
01316
01317 const Engine *e;
01318 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01319 if (HasBit(e->company_avail, _local_company)) return true;
01320 }
01321 return false;
01322 }
01323
01324
01325 const Vehicle *v;
01326 FOR_ALL_VEHICLES(v) {
01327 if (v->owner == _local_company && v->type == type) return true;
01328 }
01329
01330 return false;
01331 }
01332
01333
01334 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01335 {
01336 const Company *c = GetCompany(company);
01337 LiveryScheme scheme = LS_DEFAULT;
01338 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01339
01340
01341
01342 if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
01343
01344 const Engine *e = GetEngine(engine_type);
01345 switch (e->type) {
01346 default: NOT_REACHED();
01347 case VEH_TRAIN: {
01348 const RailVehicleInfo *rvi = RailVehInfo(engine_type);
01349 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (IsArticulatedPart(v) && rvi->railveh_type != RAILVEH_WAGON))) {
01350
01351
01352 engine_type = parent_engine_type;
01353 e = GetEngine(engine_type);
01354 rvi = RailVehInfo(engine_type);
01355
01356 }
01357
01358 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01359 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01360 if (rvi->railveh_type == RAILVEH_WAGON) {
01361 if (!GetCargo(cargo_type)->is_freight) {
01362 if (parent_engine_type == INVALID_ENGINE) {
01363 scheme = LS_PASSENGER_WAGON_STEAM;
01364 } else {
01365 switch (RailVehInfo(parent_engine_type)->engclass) {
01366 default: NOT_REACHED();
01367 case EC_STEAM: scheme = LS_PASSENGER_WAGON_STEAM; break;
01368 case EC_DIESEL: scheme = LS_PASSENGER_WAGON_DIESEL; break;
01369 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
01370 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
01371 case EC_MAGLEV: scheme = LS_PASSENGER_WAGON_MAGLEV; break;
01372 }
01373 }
01374 } else {
01375 scheme = LS_FREIGHT_WAGON;
01376 }
01377 } else {
01378 bool is_mu = HasBit(EngInfo(engine_type)->misc_flags, EF_RAIL_IS_MU);
01379
01380 switch (rvi->engclass) {
01381 default: NOT_REACHED();
01382 case EC_STEAM: scheme = LS_STEAM; break;
01383 case EC_DIESEL: scheme = is_mu ? LS_DMU : LS_DIESEL; break;
01384 case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
01385 case EC_MONORAIL: scheme = LS_MONORAIL; break;
01386 case EC_MAGLEV: scheme = LS_MAGLEV; break;
01387 }
01388 }
01389 break;
01390 }
01391
01392 case VEH_ROAD: {
01393
01394 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01395 engine_type = parent_engine_type;
01396 e = GetEngine(engine_type);
01397 cargo_type = v->First()->cargo_type;
01398 }
01399 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01400 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01401
01402
01403 if (HasBit(EngInfo(engine_type)->misc_flags, EF_ROAD_TRAM)) {
01404
01405 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01406 } else {
01407
01408 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01409 }
01410 break;
01411 }
01412
01413 case VEH_SHIP: {
01414 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01415 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01416 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01417 break;
01418 }
01419
01420 case VEH_AIRCRAFT: {
01421 switch (e->u.air.subtype) {
01422 case AIR_HELI: scheme = LS_HELICOPTER; break;
01423 case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
01424 case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
01425 }
01426 break;
01427 }
01428 }
01429
01430
01431 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01432 }
01433
01434 return &c->livery[scheme];
01435 }
01436
01437
01438 static SpriteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01439 {
01440 SpriteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01441
01442
01443 if (map != PAL_NONE) return map;
01444
01445
01446 if (HasBit(EngInfo(engine_type)->callbackmask, CBM_VEHICLE_COLOUR_REMAP)) {
01447 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01448
01449
01450 if (callback != CALLBACK_FAILED && callback != 0xC000) {
01451 map = GB(callback, 0, 14);
01452
01453
01454 if (!HasBit(callback, 14)) {
01455
01456 if (v != NULL) ((Vehicle*)v)->colourmap = map;
01457 return map;
01458 }
01459 }
01460 }
01461
01462 bool twocc = HasBit(EngInfo(engine_type)->misc_flags, EF_USES_2CC);
01463
01464 if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOUR_START;
01465
01466 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
01467
01468 map += livery->colour1;
01469 if (twocc) map += livery->colour2 * 16;
01470
01471
01472 if (v != NULL) ((Vehicle*)v)->colourmap = map;
01473 return map;
01474 }
01475
01476 SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
01477 {
01478 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01479 }
01480
01481 SpriteID GetVehiclePalette(const Vehicle *v)
01482 {
01483 if (v->type == VEH_TRAIN) {
01484 return GetEngineColourMap(v->engine_type, v->owner, v->u.rail.first_engine, v);
01485 } else if (v->type == VEH_ROAD) {
01486 return GetEngineColourMap(v->engine_type, v->owner, v->u.road.first_engine, v);
01487 }
01488
01489 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01490 }
01491
01492
01493 void Vehicle::BeginLoading()
01494 {
01495 assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01496
01497 if (this->current_order.IsType(OT_GOTO_STATION) &&
01498 this->current_order.GetDestination() == this->last_station_visited) {
01499 current_order.MakeLoading(true);
01500 UpdateVehicleTimetable(this, true);
01501
01502
01503
01504
01505
01506
01507 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01508
01509 } else {
01510 current_order.MakeLoading(false);
01511 }
01512
01513 GetStation(this->last_station_visited)->loading_vehicles.push_back(this);
01514
01515 VehiclePayment(this);
01516
01517 InvalidateWindow(GetWindowClassForVehicleType(this->type), this->owner);
01518 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01519 InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
01520 InvalidateWindow(WC_STATION_VIEW, this->last_station_visited);
01521
01522 GetStation(this->last_station_visited)->MarkTilesDirty(true);
01523 this->cur_speed = 0;
01524 this->MarkDirty();
01525 }
01526
01527 void Vehicle::LeaveStation()
01528 {
01529 assert(current_order.IsType(OT_LOADING));
01530
01531
01532 if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01533
01534 current_order.MakeLeaveStation();
01535 Station *st = GetStation(this->last_station_visited);
01536 st->loading_vehicles.remove(this);
01537
01538 HideFillingPercent(&this->fill_percent_te_id);
01539
01540 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01541
01542 if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
01543
01544
01545
01546
01547 if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(GetVehicleTrackdir(this)), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
01548 TryPathReserve(this, true, true);
01549 }
01550 }
01551 }
01552
01553
01554 void Vehicle::HandleLoading(bool mode)
01555 {
01556 switch (this->current_order.GetType()) {
01557 case OT_LOADING: {
01558 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01559
01560
01561 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01562 (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01563
01564 this->PlayLeaveStationSound();
01565
01566 bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
01567 this->LeaveStation();
01568
01569
01570 if (!at_destination_station) return;
01571 break;
01572 }
01573
01574 case OT_DUMMY: break;
01575
01576 default: return;
01577 }
01578
01579 this->cur_order_index++;
01580 InvalidateVehicleOrder(this, 0);
01581 }
01582
01583 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01584 {
01585 if (!CheckOwnership(this->owner)) return CMD_ERROR;
01586 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01587 if (this->IsStoppedInDepot()) return CMD_ERROR;
01588
01589 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01590 bool halt_in_depot = this->current_order.GetDepotActionType() & ODATFB_HALT;
01591 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01592
01593
01594
01595 if (flags & DC_EXEC) {
01596 this->current_order.SetDepotOrderType(ODTF_MANUAL);
01597 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01598 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01599 }
01600 return CommandCost();
01601 }
01602
01603 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
01604 if (flags & DC_EXEC) {
01605
01606
01607 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->cur_order_index++;
01608
01609 this->current_order.MakeDummy();
01610 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01611 }
01612 return CommandCost();
01613 }
01614
01615 TileIndex location;
01616 DestinationID destination;
01617 bool reverse;
01618 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};
01619 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01620
01621 if (flags & DC_EXEC) {
01622 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01623
01624 this->dest_tile = location;
01625 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01626 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01627 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01628
01629
01630 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01631
01632 if (this->type == VEH_AIRCRAFT && this->u.air.state == FLYING && this->u.air.targetairport != destination) {
01633
01634 extern void AircraftNextAirportPos_and_Order(Vehicle *v);
01635 AircraftNextAirportPos_and_Order(this);
01636 }
01637 }
01638
01639 return CommandCost();
01640
01641 }
01642
01643 void Vehicle::SetNext(Vehicle *next)
01644 {
01645 if (this->next != NULL) {
01646
01647 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01648 v->first = this->next;
01649 }
01650 this->next->previous = NULL;
01651 }
01652
01653 this->next = next;
01654
01655 if (this->next != NULL) {
01656
01657 if (this->next->previous != NULL) this->next->previous->next = NULL;
01658 this->next->previous = this;
01659 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01660 v->first = this->first;
01661 }
01662 }
01663 }
01664
01665 void Vehicle::AddToShared(Vehicle *shared_chain)
01666 {
01667 assert(this->previous_shared == NULL && this->next_shared == NULL);
01668
01669 if (!shared_chain->orders.list) {
01670 assert(shared_chain->previous_shared == NULL);
01671 assert(shared_chain->next_shared == NULL);
01672 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
01673 }
01674
01675 this->next_shared = shared_chain->next_shared;
01676 this->previous_shared = shared_chain;
01677
01678 shared_chain->next_shared = this;
01679
01680 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
01681
01682 shared_chain->orders.list->AddVehicle(this);
01683 }
01684
01685 void Vehicle::RemoveFromShared()
01686 {
01687
01688
01689 bool were_first = (this->FirstShared() == this);
01690 uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
01691
01692 this->orders.list->RemoveVehicle(this);
01693
01694 if (!were_first) {
01695
01696 this->previous_shared->next_shared = this->NextShared();
01697 }
01698
01699 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
01700
01701
01702 if (this->orders.list->GetNumVehicles() == 1) {
01703
01704 DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
01705 InvalidateVehicleOrder(this->FirstShared(), 0);
01706 } else if (were_first) {
01707
01708
01709 InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
01710 }
01711
01712 this->next_shared = NULL;
01713 this->previous_shared = NULL;
01714 }
01715
01716 void StopAllVehicles()
01717 {
01718 Vehicle *v;
01719 FOR_ALL_VEHICLES(v) {
01720
01721
01722 v->vehstatus |= VS_STOPPED;
01723 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01724 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
01725 }
01726 }
01727
01728 void VehiclesYearlyLoop()
01729 {
01730 Vehicle *v;
01731 FOR_ALL_VEHICLES(v) {
01732 if (v->IsPrimaryVehicle()) {
01733
01734 Money profit = v->GetDisplayProfitThisYear();
01735 if (v->age >= 730 && profit < 0) {
01736 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
01737 SetDParam(0, v->index);
01738 SetDParam(1, profit);
01739 AddNewsItem(
01740 STR_VEHICLE_IS_UNPROFITABLE,
01741 NS_ADVICE,
01742 v->index,
01743 0);
01744 }
01745 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
01746 }
01747
01748 v->profit_last_year = v->profit_this_year;
01749 v->profit_this_year = 0;
01750 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01751 }
01752 }
01753 }
01754
01755
01765 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
01766 {
01767 assert(IsEngineIndex(engine_type));
01768 const Engine *e = GetEngine(engine_type);
01769
01770 switch (e->type) {
01771 case VEH_TRAIN:
01772 return (st->facilities & FACIL_TRAIN) != 0;
01773
01774 case VEH_ROAD:
01775
01776
01777
01778 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
01779
01780 case VEH_SHIP:
01781 return (st->facilities & FACIL_DOCK) != 0;
01782
01783 case VEH_AIRCRAFT:
01784 return (st->facilities & FACIL_AIRPORT) != 0 &&
01785 (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
01786
01787 default:
01788 return false;
01789 }
01790 }
01791
01798 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
01799 {
01800 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(v) != NULL;
01801
01802 return CanVehicleUseStation(v->engine_type, st);
01803 }