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