00001
00002
00005 #include "stdafx.h"
00006 #include "landscape.h"
00007 #include "roadveh.h"
00008 #include "station_map.h"
00009 #include "command_func.h"
00010 #include "news_func.h"
00011 #include "pathfind.h"
00012 #include "npf.h"
00013 #include "company_func.h"
00014 #include "vehicle_gui.h"
00015 #include "articulated_vehicles.h"
00016 #include "newgrf_engine.h"
00017 #include "newgrf_sound.h"
00018 #include "yapf/yapf.h"
00019 #include "strings_func.h"
00020 #include "tunnelbridge_map.h"
00021 #include "functions.h"
00022 #include "window_func.h"
00023 #include "date_func.h"
00024 #include "vehicle_func.h"
00025 #include "sound_func.h"
00026 #include "variables.h"
00027 #include "autoreplace_gui.h"
00028 #include "gfx_func.h"
00029 #include "ai/ai.hpp"
00030 #include "depot_base.h"
00031 #include "effectvehicle_func.h"
00032 #include "settings_type.h"
00033
00034 #include "table/strings.h"
00035 #include "table/sprites.h"
00036
00037 static const uint16 _roadveh_images[63] = {
00038 0xCD4, 0xCDC, 0xCE4, 0xCEC, 0xCF4, 0xCFC, 0xD0C, 0xD14,
00039 0xD24, 0xD1C, 0xD2C, 0xD04, 0xD1C, 0xD24, 0xD6C, 0xD74,
00040 0xD7C, 0xC14, 0xC1C, 0xC24, 0xC2C, 0xC34, 0xC3C, 0xC4C,
00041 0xC54, 0xC64, 0xC5C, 0xC6C, 0xC44, 0xC5C, 0xC64, 0xCAC,
00042 0xCB4, 0xCBC, 0xD94, 0xD9C, 0xDA4, 0xDAC, 0xDB4, 0xDBC,
00043 0xDCC, 0xDD4, 0xDE4, 0xDDC, 0xDEC, 0xDC4, 0xDDC, 0xDE4,
00044 0xE2C, 0xE34, 0xE3C, 0xC14, 0xC1C, 0xC2C, 0xC3C, 0xC4C,
00045 0xC5C, 0xC64, 0xC6C, 0xC74, 0xC84, 0xC94, 0xCA4
00046 };
00047
00048 static const uint16 _roadveh_full_adder[63] = {
00049 0, 88, 0, 0, 0, 0, 48, 48,
00050 48, 48, 0, 0, 64, 64, 0, 16,
00051 16, 0, 88, 0, 0, 0, 0, 48,
00052 48, 48, 48, 0, 0, 64, 64, 0,
00053 16, 16, 0, 88, 0, 0, 0, 0,
00054 48, 48, 48, 48, 0, 0, 64, 64,
00055 0, 16, 16, 0, 8, 8, 8, 8,
00056 0, 0, 0, 8, 8, 8, 8
00057 };
00058
00060 static const TrackdirBits _road_enter_dir_to_reachable_trackdirs[DIAGDIR_END] = {
00061 TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_LOWER_E | TRACKDIR_BIT_X_NE,
00062 TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_UPPER_E | TRACKDIR_BIT_Y_SE,
00063 TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_X_SW | TRACKDIR_BIT_RIGHT_S,
00064 TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_Y_NW
00065 };
00066
00067 static const Trackdir _road_reverse_table[DIAGDIR_END] = {
00068 TRACKDIR_RVREV_NE, TRACKDIR_RVREV_SE, TRACKDIR_RVREV_SW, TRACKDIR_RVREV_NW
00069 };
00070
00073 static const TrackdirBits _road_exit_dir_to_incoming_trackdirs[DIAGDIR_END] = {
00074 TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_X_SW | TRACKDIR_BIT_LEFT_S,
00075 TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_Y_NW,
00076 TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_UPPER_E | TRACKDIR_BIT_X_NE,
00077 TRACKDIR_BIT_RIGHT_S | TRACKDIR_BIT_LOWER_E | TRACKDIR_BIT_Y_SE
00078 };
00079
00081 static const Trackdir _roadveh_depot_exit_trackdir[DIAGDIR_END] = {
00082 TRACKDIR_X_NE, TRACKDIR_Y_SE, TRACKDIR_X_SW, TRACKDIR_Y_NW
00083 };
00084
00085 static SpriteID GetRoadVehIcon(EngineID engine)
00086 {
00087 uint8 spritenum = RoadVehInfo(engine)->image_index;
00088
00089 if (is_custom_sprite(spritenum)) {
00090 SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W);
00091 if (sprite != 0) return sprite;
00092
00093 spritenum = GetEngine(engine)->image_index;
00094 }
00095
00096 return 6 + _roadveh_images[spritenum];
00097 }
00098
00099 SpriteID RoadVehicle::GetImage(Direction direction) const
00100 {
00101 uint8 spritenum = this->spritenum;
00102 SpriteID sprite;
00103
00104 if (is_custom_sprite(spritenum)) {
00105 sprite = GetCustomVehicleSprite(this, (Direction)(direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(spritenum)));
00106 if (sprite != 0) return sprite;
00107
00108 spritenum = GetEngine(this->engine_type)->image_index;
00109 }
00110
00111 sprite = direction + _roadveh_images[spritenum];
00112
00113 if (this->cargo.Count() >= this->cargo_cap / 2U) sprite += _roadveh_full_adder[spritenum];
00114
00115 return sprite;
00116 }
00117
00118 void DrawRoadVehEngine(int x, int y, EngineID engine, SpriteID pal)
00119 {
00120 DrawSprite(GetRoadVehIcon(engine), pal, x, y);
00121 }
00122
00123 byte GetRoadVehLength(const Vehicle *v)
00124 {
00125 byte length = 8;
00126
00127 uint16 veh_len = GetVehicleCallback(CBID_VEHICLE_LENGTH, 0, 0, v->engine_type, v);
00128 if (veh_len != CALLBACK_FAILED) {
00129 length -= Clamp(veh_len, 0, 7);
00130 }
00131
00132 return length;
00133 }
00134
00135 void RoadVehUpdateCache(Vehicle *v)
00136 {
00137 assert(v->type == VEH_ROAD);
00138 assert(IsRoadVehFront(v));
00139
00140 v->InvalidateNewGRFCacheOfChain();
00141
00142 for (Vehicle *u = v; u != NULL; u = u->Next()) {
00143
00144 assert(u->First() == v);
00145
00146
00147 u->u.road.first_engine = (v == u) ? INVALID_ENGINE : v->engine_type;
00148
00149
00150 u->u.road.cached_veh_length = GetRoadVehLength(u);
00151
00152
00153 u->colourmap = PAL_NONE;
00154 }
00155 }
00156
00163 CommandCost CmdBuildRoadVeh(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00164 {
00165 Vehicle *v;
00166 UnitID unit_num;
00167
00168 if (!IsEngineBuildable(p1, VEH_ROAD, _current_company)) return_cmd_error(STR_ROAD_VEHICLE_NOT_AVAILABLE);
00169
00170 const Engine *e = GetEngine(p1);
00171
00172 if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
00173
00174 CommandCost cost(EXPENSES_NEW_VEHICLES, e->GetCost());
00175 if (flags & DC_QUERY_COST) return cost;
00176
00177
00178
00179 if (!IsRoadDepotTile(tile)) return CMD_ERROR;
00180 if (!IsTileOwner(tile, _current_company)) return CMD_ERROR;
00181
00182 if (HasTileRoadType(tile, ROADTYPE_TRAM) != HasBit(EngInfo(p1)->misc_flags, EF_ROAD_TRAM)) return_cmd_error(STR_DEPOT_WRONG_DEPOT_TYPE);
00183
00184 uint num_vehicles = 1 + CountArticulatedParts(p1, false);
00185
00186
00187 Vehicle **vl = AllocaM(Vehicle*, num_vehicles + 1);
00188 memset(vl, 0, sizeof(*vl) * (num_vehicles + 1));
00189
00190 if (!Vehicle::AllocateList(vl, num_vehicles)) {
00191 return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
00192 }
00193
00194 v = vl[0];
00195
00196
00197 unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_ROAD);
00198 if (unit_num > _settings_game.vehicle.max_roadveh)
00199 return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
00200
00201 if (flags & DC_EXEC) {
00202 int x;
00203 int y;
00204
00205 const RoadVehicleInfo *rvi = RoadVehInfo(p1);
00206
00207 v = new (v) RoadVehicle();
00208 v->unitnumber = unit_num;
00209 v->direction = DiagDirToDir(GetRoadDepotDirection(tile));
00210 v->owner = _current_company;
00211
00212 v->tile = tile;
00213 x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
00214 y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
00215 v->x_pos = x;
00216 v->y_pos = y;
00217 v->z_pos = GetSlopeZ(x, y);
00218
00219 v->running_ticks = 0;
00220
00221 v->u.road.state = RVSB_IN_DEPOT;
00222 v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
00223
00224 v->spritenum = rvi->image_index;
00225 v->cargo_type = e->GetDefaultCargoType();
00226 v->cargo_subtype = 0;
00227 v->cargo_cap = rvi->capacity;
00228
00229 v->value = cost.GetCost();
00230
00231
00232
00233
00234
00235
00236
00237 v->last_station_visited = INVALID_STATION;
00238 v->max_speed = rvi->max_speed;
00239 v->engine_type = (EngineID)p1;
00240
00241 v->reliability = e->reliability;
00242 v->reliability_spd_dec = e->reliability_spd_dec;
00243 v->max_age = e->lifelength * DAYS_IN_LEAP_YEAR;
00244 _new_vehicle_id = v->index;
00245
00246 v->name = NULL;
00247
00248 v->service_interval = _settings_game.vehicle.servint_roadveh;
00249
00250 v->date_of_last_service = _date;
00251 v->build_year = _cur_year;
00252
00253 v->cur_image = 0xC15;
00254 v->random_bits = VehicleRandomBits();
00255 SetRoadVehFront(v);
00256
00257 v->u.road.roadtype = HasBit(EngInfo(v->engine_type)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
00258 v->u.road.compatible_roadtypes = RoadTypeToRoadTypes(v->u.road.roadtype);
00259 v->u.road.cached_veh_length = 8;
00260
00261 v->vehicle_flags = 0;
00262 if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
00263
00264 v->cargo_cap = rvi->capacity;
00265
00266 AddArticulatedParts(vl, VEH_ROAD);
00267 v->InvalidateNewGRFCacheOfChain();
00268
00269
00270 for (Vehicle *u = v; u != NULL; u = u->Next()) {
00271 u->u.road.cached_veh_length = GetRoadVehLength(u);
00272
00273 if (u->cargo_cap != 0) u->cargo_cap = GetVehicleProperty(u, 0x0F, u->cargo_cap);
00274 v->InvalidateNewGRFCache();
00275 u->InvalidateNewGRFCache();
00276 }
00277
00278 VehicleMove(v, false);
00279
00280 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
00281 InvalidateWindowClassesData(WC_ROADVEH_LIST, 0);
00282 InvalidateWindow(WC_COMPANY, v->owner);
00283 if (IsLocalCompany()) {
00284 InvalidateAutoreplaceWindow(v->engine_type, v->group_id);
00285 }
00286
00287 GetCompany(_current_company)->num_engines[p1]++;
00288
00289 CheckConsistencyOfArticulatedVehicle(v);
00290 }
00291
00292 return cost;
00293 }
00294
00295 void ClearSlot(Vehicle *v)
00296 {
00297 RoadStop *rs = v->u.road.slot;
00298 if (v->u.road.slot == NULL) return;
00299
00300 v->u.road.slot = NULL;
00301 v->u.road.slot_age = 0;
00302
00303 assert(rs->num_vehicles != 0);
00304 rs->num_vehicles--;
00305
00306 DEBUG(ms, 3, "Clearing slot at 0x%X", rs->xy);
00307 }
00308
00309 bool RoadVehicle::IsStoppedInDepot() const
00310 {
00311 TileIndex tile = this->tile;
00312
00313 if (!IsRoadDepotTile(tile)) return false;
00314 if (IsRoadVehFront(this) && !(this->vehstatus & VS_STOPPED)) return false;
00315
00316 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
00317 if (v->u.road.state != RVSB_IN_DEPOT || v->tile != tile) return false;
00318 }
00319 return true;
00320 }
00321
00328 CommandCost CmdSellRoadVeh(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00329 {
00330 Vehicle *v;
00331
00332 if (!IsValidVehicleID(p1)) return CMD_ERROR;
00333
00334 v = GetVehicle(p1);
00335
00336 if (v->type != VEH_ROAD || !CheckOwnership(v->owner)) return CMD_ERROR;
00337
00338 if (HASBITS(v->vehstatus, VS_CRASHED)) return_cmd_error(STR_CAN_T_SELL_DESTROYED_VEHICLE);
00339
00340 if (!v->IsStoppedInDepot()) {
00341 return_cmd_error(STR_9013_MUST_BE_STOPPED_INSIDE);
00342 }
00343
00344 CommandCost ret(EXPENSES_NEW_VEHICLES, -v->value);
00345
00346 if (flags & DC_EXEC) {
00347 delete v;
00348 }
00349
00350 return ret;
00351 }
00352
00353 struct RoadFindDepotData {
00354 uint best_length;
00355 TileIndex tile;
00356 OwnerByte owner;
00357 };
00358
00359 static const DiagDirection _road_pf_directions[] = {
00360 DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE, INVALID_DIAGDIR, INVALID_DIAGDIR,
00361 DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE, INVALID_DIAGDIR, INVALID_DIAGDIR
00362 };
00363
00364 static bool EnumRoadSignalFindDepot(TileIndex tile, void *data, Trackdir trackdir, uint length)
00365 {
00366 RoadFindDepotData *rfdd = (RoadFindDepotData*)data;
00367
00368 tile += TileOffsByDiagDir(_road_pf_directions[trackdir]);
00369
00370 if (IsRoadDepotTile(tile) &&
00371 IsTileOwner(tile, rfdd->owner) &&
00372 length < rfdd->best_length) {
00373 rfdd->best_length = length;
00374 rfdd->tile = tile;
00375 }
00376 return false;
00377 }
00378
00379 static const Depot *FindClosestRoadDepot(const Vehicle *v)
00380 {
00381 switch (_settings_game.pf.pathfinder_for_roadvehs) {
00382 case VPF_YAPF:
00383 return YapfFindNearestRoadDepot(v);
00384
00385 case VPF_NPF: {
00386
00387 Trackdir trackdir = GetVehicleTrackdir(v);
00388
00389 NPFFoundTargetData ftd = NPFRouteToDepotBreadthFirstTwoWay(v->tile, trackdir, false, v->tile, ReverseTrackdir(trackdir), false, TRANSPORT_ROAD, v->u.road.compatible_roadtypes, v->owner, INVALID_RAILTYPES, 0);
00390
00391 if (ftd.best_bird_dist == 0) return GetDepotByTile(ftd.node.tile);
00392 } break;
00393
00394 default:
00395 case VPF_OPF: {
00396 RoadFindDepotData rfdd;
00397
00398 rfdd.owner = v->owner;
00399 rfdd.best_length = UINT_MAX;
00400
00401
00402 for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) {
00403 FollowTrack(v->tile, PATHFIND_FLAGS_NONE, TRANSPORT_ROAD, v->u.road.compatible_roadtypes, d, EnumRoadSignalFindDepot, NULL, &rfdd);
00404 }
00405
00406 if (rfdd.best_length != UINT_MAX) return GetDepotByTile(rfdd.tile);
00407 } break;
00408 }
00409
00410 return NULL;
00411 }
00412
00413 bool RoadVehicle::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
00414 {
00415 const Depot *depot = FindClosestRoadDepot(this);
00416
00417 if (depot == NULL) return false;
00418
00419 if (location != NULL) *location = depot->xy;
00420 if (destination != NULL) *destination = depot->index;
00421
00422 return true;
00423 }
00424
00433 CommandCost CmdSendRoadVehToDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00434 {
00435 if (p2 & DEPOT_MASS_SEND) {
00436
00437 if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
00438 return SendAllVehiclesToDepot(VEH_ROAD, flags, p2 & DEPOT_SERVICE, _current_company, (p2 & VLW_MASK), p1);
00439 }
00440
00441 if (!IsValidVehicleID(p1)) return CMD_ERROR;
00442
00443 Vehicle *v = GetVehicle(p1);
00444
00445 if (v->type != VEH_ROAD) return CMD_ERROR;
00446
00447 return v->SendToDepot(flags, (DepotCommand)(p2 & DEPOT_COMMAND_MASK));
00448 }
00449
00456 CommandCost CmdTurnRoadVeh(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00457 {
00458 Vehicle *v;
00459
00460 if (!IsValidVehicleID(p1)) return CMD_ERROR;
00461
00462 v = GetVehicle(p1);
00463
00464 if (v->type != VEH_ROAD || !CheckOwnership(v->owner)) return CMD_ERROR;
00465
00466 if (v->vehstatus & VS_STOPPED ||
00467 v->vehstatus & VS_CRASHED ||
00468 v->breakdown_ctr != 0 ||
00469 v->u.road.overtaking != 0 ||
00470 v->u.road.state == RVSB_WORMHOLE ||
00471 v->IsInDepot() ||
00472 v->cur_speed < 5) {
00473 return CMD_ERROR;
00474 }
00475
00476 if (IsNormalRoadTile(v->tile) && GetDisallowedRoadDirections(v->tile) != DRD_NONE) return CMD_ERROR;
00477
00478 if (IsTileType(v->tile, MP_TUNNELBRIDGE) && DirToDiagDir(v->direction) == GetTunnelBridgeDirection(v->tile)) return CMD_ERROR;
00479
00480 if (flags & DC_EXEC) v->u.road.reverse_ctr = 180;
00481
00482 return CommandCost();
00483 }
00484
00485
00486 void RoadVehicle::MarkDirty()
00487 {
00488 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00489 v->cur_image = v->GetImage(v->direction);
00490 MarkSingleVehicleDirty(v);
00491 }
00492 }
00493
00494 void RoadVehicle::UpdateDeltaXY(Direction direction)
00495 {
00496 #define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
00497 static const uint32 _delta_xy_table[8] = {
00498 MKIT(3, 3, -1, -1),
00499 MKIT(3, 7, -1, -3),
00500 MKIT(3, 3, -1, -1),
00501 MKIT(7, 3, -3, -1),
00502 MKIT(3, 3, -1, -1),
00503 MKIT(3, 7, -1, -3),
00504 MKIT(3, 3, -1, -1),
00505 MKIT(7, 3, -3, -1),
00506 };
00507 #undef MKIT
00508
00509 uint32 x = _delta_xy_table[direction];
00510 this->x_offs = GB(x, 0, 8);
00511 this->y_offs = GB(x, 8, 8);
00512 this->x_extent = GB(x, 16, 8);
00513 this->y_extent = GB(x, 24, 8);
00514 this->z_extent = 6;
00515 }
00516
00517 static void ClearCrashedStation(Vehicle *v)
00518 {
00519 RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile));
00520
00521
00522 rs->SetEntranceBusy(false);
00523
00524
00525 rs->FreeBay(HasBit(v->u.road.state, RVS_USING_SECOND_BAY));
00526 }
00527
00528 static void DeleteLastRoadVeh(Vehicle *v)
00529 {
00530 Vehicle *u = v;
00531 for (; v->Next() != NULL; v = v->Next()) u = v;
00532 u->SetNext(NULL);
00533
00534 if (IsTileType(v->tile, MP_STATION)) ClearCrashedStation(v);
00535
00536 delete v;
00537 }
00538
00539 static byte SetRoadVehPosition(Vehicle *v, int x, int y)
00540 {
00541 byte new_z, old_z;
00542
00543
00544 v->x_pos = x;
00545 v->y_pos = y;
00546 new_z = GetSlopeZ(x, y);
00547
00548 old_z = v->z_pos;
00549 v->z_pos = new_z;
00550
00551 VehicleMove(v, true);
00552 return old_z;
00553 }
00554
00555 static void RoadVehSetRandomDirection(Vehicle *v)
00556 {
00557 static const DirDiff delta[] = {
00558 DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
00559 };
00560
00561 do {
00562 uint32 r = Random();
00563
00564 v->direction = ChangeDir(v->direction, delta[r & 3]);
00565 v->UpdateDeltaXY(v->direction);
00566 v->cur_image = v->GetImage(v->direction);
00567 SetRoadVehPosition(v, v->x_pos, v->y_pos);
00568 } while ((v = v->Next()) != NULL);
00569 }
00570
00571 static void RoadVehIsCrashed(Vehicle *v)
00572 {
00573 v->u.road.crashed_ctr++;
00574 if (v->u.road.crashed_ctr == 2) {
00575 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
00576 } else if (v->u.road.crashed_ctr <= 45) {
00577 if ((v->tick_counter & 7) == 0) RoadVehSetRandomDirection(v);
00578 } else if (v->u.road.crashed_ctr >= 2220 && !(v->tick_counter & 0x1F)) {
00579 DeleteLastRoadVeh(v);
00580 }
00581 }
00582
00583 static Vehicle *EnumCheckRoadVehCrashTrain(Vehicle *v, void *data)
00584 {
00585 const Vehicle *u = (Vehicle*)data;
00586
00587 return
00588 v->type == VEH_TRAIN &&
00589 abs(v->z_pos - u->z_pos) <= 6 &&
00590 abs(v->x_pos - u->x_pos) <= 4 &&
00591 abs(v->y_pos - u->y_pos) <= 4 ?
00592 v : NULL;
00593 }
00594
00595 static void RoadVehCrash(Vehicle *v)
00596 {
00597 uint16 pass = 1;
00598
00599 v->u.road.crashed_ctr++;
00600
00601 for (Vehicle *u = v; u != NULL; u = u->Next()) {
00602 if (IsCargoInClass(u->cargo_type, CC_PASSENGERS)) pass += u->cargo.Count();
00603
00604 u->vehstatus |= VS_CRASHED;
00605
00606 MarkSingleVehicleDirty(u);
00607 }
00608
00609 ClearSlot(v);
00610
00611 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00612
00613 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, AIEventVehicleCrashed::CRASH_RV_LEVEL_CROSSING));
00614
00615 SetDParam(0, pass);
00616 AddNewsItem(
00617 (pass == 1) ?
00618 STR_9031_ROAD_VEHICLE_CRASH_DRIVER : STR_9032_ROAD_VEHICLE_CRASH_DIE,
00619 NS_ACCIDENT_VEHICLE,
00620 v->index,
00621 0
00622 );
00623
00624 ModifyStationRatingAround(v->tile, v->owner, -160, 22);
00625 SndPlayVehicleFx(SND_12_EXPLOSION, v);
00626 }
00627
00628 static bool RoadVehCheckTrainCrash(Vehicle *v)
00629 {
00630 for (Vehicle *u = v; u != NULL; u = u->Next()) {
00631 if (u->u.road.state == RVSB_WORMHOLE) continue;
00632
00633 TileIndex tile = u->tile;
00634
00635 if (!IsLevelCrossingTile(tile)) continue;
00636
00637 if (HasVehicleOnPosXY(v->x_pos, v->y_pos, u, EnumCheckRoadVehCrashTrain)) {
00638 RoadVehCrash(v);
00639 return true;
00640 }
00641 }
00642
00643 return false;
00644 }
00645
00646 static void HandleBrokenRoadVeh(Vehicle *v)
00647 {
00648 if (v->breakdown_ctr != 1) {
00649 v->breakdown_ctr = 1;
00650 v->cur_speed = 0;
00651
00652 if (v->breakdowns_since_last_service != 255)
00653 v->breakdowns_since_last_service++;
00654
00655 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
00656 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00657
00658 if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
00659 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
00660 SND_0F_VEHICLE_BREAKDOWN : SND_35_COMEDY_BREAKDOWN, v);
00661 }
00662
00663 if (!(v->vehstatus & VS_HIDDEN)) {
00664 Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
00665 if (u != NULL) u->u.effect.animation_state = v->breakdown_delay * 2;
00666 }
00667 }
00668
00669 if ((v->tick_counter & 1) == 0) {
00670 if (--v->breakdown_delay == 0) {
00671 v->breakdown_ctr = 0;
00672 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
00673 }
00674 }
00675 }
00676
00677 TileIndex RoadVehicle::GetOrderStationLocation(StationID station)
00678 {
00679 if (station == this->last_station_visited) this->last_station_visited = INVALID_STATION;
00680
00681 TileIndex dest = INVALID_TILE;
00682 const RoadStop *rs = GetStation(station)->GetPrimaryRoadStop(this);
00683 if (rs != NULL) {
00684 uint mindist = UINT_MAX;
00685
00686 for (; rs != NULL; rs = rs->GetNextRoadStop(this)) {
00687 uint dist = DistanceManhattan(this->tile, rs->xy);
00688
00689 if (dist < mindist) {
00690 mindist = dist;
00691 dest = rs->xy;
00692 }
00693 }
00694 }
00695
00696 if (dest != INVALID_TILE) {
00697 return dest;
00698 } else {
00699
00700 this->cur_order_index++;
00701 return 0;
00702 }
00703 }
00704
00705 static void StartRoadVehSound(const Vehicle *v)
00706 {
00707 if (!PlayVehicleSound(v, VSE_START)) {
00708 SoundFx s = RoadVehInfo(v->engine_type)->sfx;
00709 if (s == SND_19_BUS_START_PULL_AWAY && (v->tick_counter & 3) == 0)
00710 s = SND_1A_BUS_START_PULL_AWAY_WITH_HORN;
00711 SndPlayVehicleFx(s, v);
00712 }
00713 }
00714
00715 struct RoadVehFindData {
00716 int x;
00717 int y;
00718 const Vehicle *veh;
00719 Vehicle *best;
00720 uint best_diff;
00721 Direction dir;
00722 };
00723
00724 static Vehicle *EnumCheckRoadVehClose(Vehicle *v, void *data)
00725 {
00726 static const int8 dist_x[] = { -4, -8, -4, -1, 4, 8, 4, 1 };
00727 static const int8 dist_y[] = { -4, -1, 4, 8, 4, 1, -4, -8 };
00728
00729 RoadVehFindData *rvf = (RoadVehFindData*)data;
00730
00731 short x_diff = v->x_pos - rvf->x;
00732 short y_diff = v->y_pos - rvf->y;
00733
00734 if (v->type == VEH_ROAD &&
00735 !v->IsInDepot() &&
00736 abs(v->z_pos - rvf->veh->z_pos) < 6 &&
00737 v->direction == rvf->dir &&
00738 rvf->veh->First() != v->First() &&
00739 (dist_x[v->direction] >= 0 || (x_diff > dist_x[v->direction] && x_diff <= 0)) &&
00740 (dist_x[v->direction] <= 0 || (x_diff < dist_x[v->direction] && x_diff >= 0)) &&
00741 (dist_y[v->direction] >= 0 || (y_diff > dist_y[v->direction] && y_diff <= 0)) &&
00742 (dist_y[v->direction] <= 0 || (y_diff < dist_y[v->direction] && y_diff >= 0))) {
00743 uint diff = abs(x_diff) + abs(y_diff);
00744
00745 if (diff < rvf->best_diff || (diff == rvf->best_diff && v->index < rvf->best->index)) {
00746 rvf->best = v;
00747 rvf->best_diff = diff;
00748 }
00749 }
00750
00751 return NULL;
00752 }
00753
00754 static Vehicle *RoadVehFindCloseTo(Vehicle *v, int x, int y, Direction dir)
00755 {
00756 RoadVehFindData rvf;
00757 Vehicle *front = v->First();
00758
00759 if (front->u.road.reverse_ctr != 0) return NULL;
00760
00761 rvf.x = x;
00762 rvf.y = y;
00763 rvf.dir = dir;
00764 rvf.veh = v;
00765 rvf.best_diff = UINT_MAX;
00766
00767 if (front->u.road.state == RVSB_WORMHOLE) {
00768 FindVehicleOnPos(v->tile, &rvf, EnumCheckRoadVehClose);
00769 FindVehicleOnPos(GetOtherTunnelBridgeEnd(v->tile), &rvf, EnumCheckRoadVehClose);
00770 } else {
00771 FindVehicleOnPosXY(x, y, &rvf, EnumCheckRoadVehClose);
00772 }
00773
00774
00775
00776
00777
00778 if (rvf.best_diff == UINT_MAX) {
00779 front->u.road.blocked_ctr = 0;
00780 return NULL;
00781 }
00782
00783 if (++front->u.road.blocked_ctr > 1480) return NULL;
00784
00785 return rvf.best;
00786 }
00787
00788 static void RoadVehArrivesAt(const Vehicle *v, Station *st)
00789 {
00790 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
00791
00792 if (!(st->had_vehicle_of_type & HVOT_BUS)) {
00793 st->had_vehicle_of_type |= HVOT_BUS;
00794 SetDParam(0, st->index);
00795 AddNewsItem(
00796 v->u.road.roadtype == ROADTYPE_ROAD ? STR_902F_CITIZENS_CELEBRATE_FIRST : STR_CITIZENS_CELEBRATE_FIRST_PASSENGER_TRAM,
00797 (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
00798 v->index,
00799 st->index
00800 );
00801 AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
00802 }
00803 } else {
00804
00805 if (!(st->had_vehicle_of_type & HVOT_TRUCK)) {
00806 st->had_vehicle_of_type |= HVOT_TRUCK;
00807 SetDParam(0, st->index);
00808 AddNewsItem(
00809 v->u.road.roadtype == ROADTYPE_ROAD ? STR_9030_CITIZENS_CELEBRATE_FIRST : STR_CITIZENS_CELEBRATE_FIRST_CARGO_TRAM,
00810 (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
00811 v->index,
00812 st->index
00813 );
00814 AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
00815 }
00816 }
00817 }
00818
00819 static int RoadVehAccelerate(Vehicle *v)
00820 {
00821 uint oldspeed = v->cur_speed;
00822 uint accel = 256 + (v->u.road.overtaking != 0 ? 256 : 0);
00823 uint spd = v->subspeed + accel;
00824
00825 v->subspeed = (uint8)spd;
00826
00827 int tempmax = v->max_speed;
00828 if (v->cur_speed > v->max_speed) {
00829 tempmax = v->cur_speed - (v->cur_speed / 10) - 1;
00830 }
00831
00832 v->cur_speed = spd = Clamp(v->cur_speed + ((int)spd >> 8), 0, tempmax);
00833
00834
00835 if (v->u.road.state == RVSB_WORMHOLE && !(v->vehstatus & VS_HIDDEN)) {
00836 v->cur_speed = min(v->cur_speed, GetBridgeSpec(GetBridgeType(v->tile))->speed * 2);
00837 }
00838
00839
00840 if (oldspeed != v->cur_speed) {
00841 if (_settings_client.gui.vehicle_speed) {
00842 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00843 }
00844 }
00845
00846
00847 int scaled_spd = spd * 3 >> 2;
00848
00849 scaled_spd += v->progress;
00850 v->progress = 0;
00851 return scaled_spd;
00852 }
00853
00854 static Direction RoadVehGetNewDirection(const Vehicle *v, int x, int y)
00855 {
00856 static const Direction _roadveh_new_dir[] = {
00857 DIR_N , DIR_NW, DIR_W , INVALID_DIR,
00858 DIR_NE, DIR_N , DIR_SW, INVALID_DIR,
00859 DIR_E , DIR_SE, DIR_S
00860 };
00861
00862 x = x - v->x_pos + 1;
00863 y = y - v->y_pos + 1;
00864
00865 if ((uint)x > 2 || (uint)y > 2) return v->direction;
00866 return _roadveh_new_dir[y * 4 + x];
00867 }
00868
00869 static Direction RoadVehGetSlidingDirection(const Vehicle *v, int x, int y)
00870 {
00871 Direction new_dir = RoadVehGetNewDirection(v, x, y);
00872 Direction old_dir = v->direction;
00873 DirDiff delta;
00874
00875 if (new_dir == old_dir) return old_dir;
00876 delta = (DirDifference(new_dir, old_dir) > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00877 return ChangeDir(old_dir, delta);
00878 }
00879
00880 struct OvertakeData {
00881 const Vehicle *u;
00882 const Vehicle *v;
00883 TileIndex tile;
00884 Trackdir trackdir;
00885 };
00886
00887 static Vehicle *EnumFindVehBlockingOvertake(Vehicle *v, void *data)
00888 {
00889 const OvertakeData *od = (OvertakeData*)data;
00890
00891 return
00892 v->type == VEH_ROAD && v->First() == v && v != od->u && v != od->v ?
00893 v : NULL;
00894 }
00895
00902 static bool CheckRoadBlockedForOvertaking(OvertakeData *od)
00903 {
00904 TrackStatus ts = GetTileTrackStatus(od->tile, TRANSPORT_ROAD, od->v->u.road.compatible_roadtypes);
00905 TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts);
00906 TrackdirBits red_signals = TrackStatusToRedSignals(ts);
00907 TrackBits trackbits = TrackdirBitsToTrackBits(trackdirbits);
00908
00909
00910 if (!HasBit(trackdirbits, od->trackdir) || (trackbits & ~TRACK_BIT_CROSS) || (red_signals != TRACKDIR_BIT_NONE)) return true;
00911
00912
00913 return HasVehicleOnPos(od->tile, od, EnumFindVehBlockingOvertake);
00914 }
00915
00916 static void RoadVehCheckOvertake(Vehicle *v, Vehicle *u)
00917 {
00918 OvertakeData od;
00919
00920 od.v = v;
00921 od.u = u;
00922
00923 if (u->max_speed >= v->max_speed &&
00924 !(u->vehstatus & VS_STOPPED) &&
00925 u->cur_speed != 0) {
00926 return;
00927 }
00928
00929
00930 if (v->u.road.roadtype == ROADTYPE_TRAM) return;
00931
00932
00933 if (IsTileType(v->tile, MP_STATION)) return;
00934
00935
00936 if (RoadVehHasArticPart(v)) return;
00937
00938
00939 if (v->direction != u->direction || !(v->direction & 1)) return;
00940
00941
00942 if (v->u.road.state >= RVSB_IN_ROAD_STOP || !IsStraightRoadTrackdir((Trackdir)(v->u.road.state & RVSB_TRACKDIR_MASK))) return;
00943
00944 od.trackdir = DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
00945
00946
00947
00948
00949
00950
00951
00952 od.tile = v->tile;
00953 if (CheckRoadBlockedForOvertaking(&od)) return;
00954
00955 od.tile = v->tile + TileOffsByDiagDir(DirToDiagDir(v->direction));
00956 if (CheckRoadBlockedForOvertaking(&od)) return;
00957
00958 if (od.u->cur_speed == 0 || od.u->vehstatus& VS_STOPPED) {
00959 v->u.road.overtaking_ctr = 0x11;
00960 v->u.road.overtaking = 0x10;
00961 } else {
00962
00963 v->u.road.overtaking_ctr = 0;
00964 v->u.road.overtaking = 0x10;
00965 }
00966 }
00967
00968 static void RoadZPosAffectSpeed(Vehicle *v, byte old_z)
00969 {
00970 if (old_z == v->z_pos) return;
00971
00972 if (old_z < v->z_pos) {
00973 v->cur_speed = v->cur_speed * 232 / 256;
00974 } else {
00975 uint16 spd = v->cur_speed + 2;
00976 if (spd <= v->max_speed) v->cur_speed = spd;
00977 }
00978 }
00979
00980 static int PickRandomBit(uint bits)
00981 {
00982 uint i;
00983 uint num = RandomRange(CountBits(bits));
00984
00985 for (i = 0; !(bits & 1) || (int)--num >= 0; bits >>= 1, i++) {}
00986 return i;
00987 }
00988
00989 struct FindRoadToChooseData {
00990 TileIndex dest;
00991 uint maxtracklen;
00992 uint mindist;
00993 };
00994
00995 static bool EnumRoadTrackFindDist(TileIndex tile, void *data, Trackdir trackdir, uint length)
00996 {
00997 FindRoadToChooseData *frd = (FindRoadToChooseData*)data;
00998 uint dist = DistanceManhattan(tile, frd->dest);
00999
01000 if (dist <= frd->mindist) {
01001 if (dist != frd->mindist || length < frd->maxtracklen) {
01002 frd->maxtracklen = length;
01003 }
01004 frd->mindist = dist;
01005 }
01006 return false;
01007 }
01008
01009 static inline NPFFoundTargetData PerfNPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, bool ignore_start_tile, NPFFindStationOrTileData *target, TransportType type, uint sub_type, Owner owner, RailTypes railtypes)
01010 {
01011
01012 void *perf = NpfBeginInterval();
01013 NPFFoundTargetData ret = NPFRouteToStationOrTile(tile, trackdir, ignore_start_tile, target, type, sub_type, owner, railtypes);
01014 int t = NpfEndInterval(perf);
01015 DEBUG(yapf, 4, "[NPFR] %d us - %d rounds - %d open - %d closed -- ", t, 0, _aystar_stats_open_size, _aystar_stats_closed_size);
01016 return ret;
01017 }
01018
01027 static Trackdir RoadFindPathToDest(Vehicle *v, TileIndex tile, DiagDirection enterdir)
01028 {
01029 #define return_track(x) { best_track = (Trackdir)x; goto found_best_track; }
01030
01031 TileIndex desttile;
01032 FindRoadToChooseData frd;
01033 Trackdir best_track;
01034
01035 TrackStatus ts = GetTileTrackStatus(tile, TRANSPORT_ROAD, v->u.road.compatible_roadtypes);
01036 TrackdirBits red_signals = TrackStatusToRedSignals(ts);
01037 TrackdirBits trackdirs = TrackStatusToTrackdirBits(ts);
01038
01039 if (IsTileType(tile, MP_ROAD)) {
01040 if (IsRoadDepot(tile) && (!IsTileOwner(tile, v->owner) || GetRoadDepotDirection(tile) == enterdir || (GetRoadTypes(tile) & v->u.road.compatible_roadtypes) == 0)) {
01041
01042 trackdirs = TRACKDIR_BIT_NONE;
01043 }
01044 } else if (IsTileType(tile, MP_STATION) && IsStandardRoadStopTile(tile)) {
01045
01046
01047 if (!IsTileOwner(tile, v->owner) || GetRoadStopDir(tile) == enterdir || RoadVehHasArticPart(v)) {
01048
01049 trackdirs = TRACKDIR_BIT_NONE;
01050 } else {
01051
01052 RoadStopType rstype = IsCargoInClass(v->cargo_type, CC_PASSENGERS) ? ROADSTOP_BUS : ROADSTOP_TRUCK;
01053
01054 if (GetRoadStopType(tile) != rstype) {
01055
01056 trackdirs = TRACKDIR_BIT_NONE;
01057 } else {
01058
01059 if (!_settings_game.pf.roadveh_queue && IsStandardRoadStopTile(tile) &&
01060 !GetRoadStopByTile(tile, rstype)->HasFreeBay()) {
01061
01062 trackdirs = TRACKDIR_BIT_NONE;
01063 }
01064 }
01065 }
01066 }
01067
01068
01069
01070
01071
01072
01073 trackdirs &= _road_enter_dir_to_reachable_trackdirs[enterdir];
01074 if (trackdirs == TRACKDIR_BIT_NONE) {
01075
01076 return_track(_road_reverse_table[enterdir]);
01077 }
01078
01079 if (v->u.road.reverse_ctr != 0) {
01080 bool reverse = true;
01081 if (v->u.road.roadtype == ROADTYPE_TRAM) {
01082
01083
01084 RoadBits rb = GetAnyRoadBits(tile, ROADTYPE_TRAM);
01085 RoadBits straight = AxisToRoadBits(DiagDirToAxis(enterdir));
01086 reverse = ((rb & straight) == straight) ||
01087 (rb == DiagDirToRoadBits(enterdir));
01088 }
01089 if (reverse) {
01090 v->u.road.reverse_ctr = 0;
01091 if (v->tile != tile) {
01092 return_track(_road_reverse_table[enterdir]);
01093 }
01094 }
01095 }
01096
01097 desttile = v->dest_tile;
01098 if (desttile == 0) {
01099
01100 return_track(PickRandomBit(trackdirs));
01101 }
01102
01103
01104 if (KillFirstBit(trackdirs) == TRACKDIR_BIT_NONE) {
01105 return_track(FindFirstBit2x64(trackdirs));
01106 }
01107
01108 switch (_settings_game.pf.pathfinder_for_roadvehs) {
01109 case VPF_YAPF: {
01110 Trackdir trackdir = YapfChooseRoadTrack(v, tile, enterdir);
01111 if (trackdir != INVALID_TRACKDIR) return_track(trackdir);
01112 return_track(PickRandomBit(trackdirs));
01113 } break;
01114
01115 case VPF_NPF: {
01116 NPFFindStationOrTileData fstd;
01117
01118 NPFFillWithOrderData(&fstd, v);
01119 Trackdir trackdir = DiagDirToDiagTrackdir(enterdir);
01120
01121
01122 NPFFoundTargetData ftd = PerfNPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, true, &fstd, TRANSPORT_ROAD, v->u.road.compatible_roadtypes, v->owner, INVALID_RAILTYPES);
01123 if (ftd.best_trackdir == INVALID_TRACKDIR) {
01124
01125
01126
01127 return_track(FindFirstBit2x64(trackdirs));
01128 } else {
01129
01130
01131
01132
01133 return_track(ftd.best_trackdir);
01134 }
01135 } break;
01136
01137 default:
01138 case VPF_OPF: {
01139 DiagDirection dir;
01140
01141 if (IsTileType(desttile, MP_ROAD)) {
01142 if (IsRoadDepot(desttile)) {
01143 dir = GetRoadDepotDirection(desttile);
01144 goto do_it;
01145 }
01146 } else if (IsTileType(desttile, MP_STATION)) {
01147
01148 if (IsStandardRoadStopTile(desttile)) {
01149 dir = GetRoadStopDir(desttile);
01150 do_it:;
01151
01152
01153
01154 desttile += TileOffsByDiagDir(dir);
01155 if (desttile == tile && trackdirs & _road_exit_dir_to_incoming_trackdirs[dir]) {
01156
01157
01158
01159 return_track(FindFirstBit2x64(trackdirs & _road_exit_dir_to_incoming_trackdirs[dir]));
01160 }
01161 }
01162 }
01163
01164 frd.dest = desttile;
01165
01166 best_track = INVALID_TRACKDIR;
01167 uint best_dist = UINT_MAX;
01168 uint best_maxlen = UINT_MAX;
01169 uint bitmask = (uint)trackdirs;
01170 uint i;
01171 FOR_EACH_SET_BIT(i, bitmask) {
01172 if (best_track == INVALID_TRACKDIR) best_track = (Trackdir)i;
01173 frd.maxtracklen = UINT_MAX;
01174 frd.mindist = UINT_MAX;
01175 FollowTrack(tile, PATHFIND_FLAGS_NONE, TRANSPORT_ROAD, v->u.road.compatible_roadtypes, _road_pf_directions[i], EnumRoadTrackFindDist, NULL, &frd);
01176
01177 if (frd.mindist < best_dist || (frd.mindist == best_dist && frd.maxtracklen < best_maxlen)) {
01178 best_dist = frd.mindist;
01179 best_maxlen = frd.maxtracklen;
01180 best_track = (Trackdir)i;
01181 }
01182 }
01183 } break;
01184 }
01185
01186 found_best_track:;
01187
01188 if (HasBit(red_signals, best_track)) return INVALID_TRACKDIR;
01189
01190 return best_track;
01191 }
01192
01193 static uint RoadFindPathToStop(const Vehicle *v, TileIndex tile)
01194 {
01195 if (_settings_game.pf.pathfinder_for_roadvehs == VPF_YAPF) {
01196
01197 return YapfRoadVehDistanceToTile(v, tile);
01198 }
01199
01200
01201 Trackdir trackdir = GetVehicleTrackdir(v);
01202 assert(trackdir != INVALID_TRACKDIR);
01203
01204 NPFFindStationOrTileData fstd;
01205 fstd.dest_coords = tile;
01206 fstd.station_index = INVALID_STATION;
01207
01208 uint dist = NPFRouteToStationOrTile(v->tile, trackdir, false, &fstd, TRANSPORT_ROAD, v->u.road.compatible_roadtypes, v->owner, INVALID_RAILTYPES).best_path_dist;
01209
01210 if (dist != UINT_MAX) dist = (dist + NPF_TILE_LENGTH - 1) / NPF_TILE_LENGTH;
01211
01212 return dist;
01213 }
01214
01215 struct RoadDriveEntry {
01216 byte x, y;
01217 };
01218
01219 #include "table/roadveh_movement.h"
01220
01221 static const byte _road_veh_data_1[] = {
01222 20, 20, 16, 16, 0, 0, 0, 0,
01223 19, 19, 15, 15, 0, 0, 0, 0,
01224 16, 16, 12, 12, 0, 0, 0, 0,
01225 15, 15, 11, 11
01226 };
01227
01228 static bool RoadVehLeaveDepot(Vehicle *v, bool first)
01229 {
01230
01231 for (const Vehicle *u = v; u != NULL; u = u->Next()) {
01232 if (u->u.road.state != RVSB_IN_DEPOT || u->tile != v->tile) return false;
01233 }
01234
01235 DiagDirection dir = GetRoadDepotDirection(v->tile);
01236 v->direction = DiagDirToDir(dir);
01237
01238 Trackdir tdir = _roadveh_depot_exit_trackdir[dir];
01239 const RoadDriveEntry *rdp = _road_drive_data[v->u.road.roadtype][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + tdir];
01240
01241 int x = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF);
01242 int y = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF);
01243
01244 if (first) {
01245 if (RoadVehFindCloseTo(v, x, y, v->direction) != NULL) return true;
01246
01247 VehicleServiceInDepot(v);
01248
01249 StartRoadVehSound(v);
01250
01251
01252 v->cur_speed = 0;
01253 }
01254
01255 v->vehstatus &= ~VS_HIDDEN;
01256 v->u.road.state = tdir;
01257 v->u.road.frame = RVC_DEPOT_START_FRAME;
01258
01259 v->UpdateDeltaXY(v->direction);
01260 SetRoadVehPosition(v, x, y);
01261
01262 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01263
01264 return true;
01265 }
01266
01267 static Trackdir FollowPreviousRoadVehicle(const Vehicle *v, const Vehicle *prev, TileIndex tile, DiagDirection entry_dir, bool already_reversed)
01268 {
01269 if (prev->tile == v->tile && !already_reversed) {
01270
01271
01272 return _road_reverse_table[entry_dir];
01273 }
01274
01275 byte prev_state = prev->u.road.state;
01276 Trackdir dir;
01277
01278 if (prev_state == RVSB_WORMHOLE || prev_state == RVSB_IN_DEPOT) {
01279 DiagDirection diag_dir = INVALID_DIAGDIR;
01280
01281 if (IsTileType(tile, MP_TUNNELBRIDGE)) {
01282 diag_dir = GetTunnelBridgeDirection(tile);
01283 } else if (IsRoadDepotTile(tile)) {
01284 diag_dir = ReverseDiagDir(GetRoadDepotDirection(tile));
01285 }
01286
01287 if (diag_dir == INVALID_DIAGDIR) return INVALID_TRACKDIR;
01288 dir = DiagDirToDiagTrackdir(diag_dir);
01289 } else {
01290 if (already_reversed && prev->tile != tile) {
01291
01292
01293
01294
01295
01296
01297
01298
01299
01300
01301
01302
01303
01304
01305
01306 Trackdir reversed_turn_lookup[2][DIAGDIR_END] = {
01307 { TRACKDIR_UPPER_W, TRACKDIR_RIGHT_N, TRACKDIR_LEFT_N, TRACKDIR_UPPER_E },
01308 { TRACKDIR_RIGHT_S, TRACKDIR_LOWER_W, TRACKDIR_LOWER_E, TRACKDIR_LEFT_S }};
01309 dir = reversed_turn_lookup[prev->tile < tile ? 0 : 1][ReverseDiagDir(entry_dir)];
01310 } else if (HasBit(prev_state, RVS_IN_DT_ROAD_STOP)) {
01311 dir = (Trackdir)(prev_state & RVSB_ROAD_STOP_TRACKDIR_MASK);
01312 } else if (prev_state < TRACKDIR_END) {
01313 dir = (Trackdir)prev_state;
01314 } else {
01315 return INVALID_TRACKDIR;
01316 }
01317 }
01318
01319
01320 static const RoadBits required_roadbits[] = {
01321 ROAD_X, ROAD_Y, ROAD_NW | ROAD_NE, ROAD_SW | ROAD_SE,
01322 ROAD_NW | ROAD_SW, ROAD_NE | ROAD_SE, ROAD_X, ROAD_Y
01323 };
01324 RoadBits required = required_roadbits[dir & 0x07];
01325
01326 if ((required & GetAnyRoadBits(tile, v->u.road.roadtype, true)) == ROAD_NONE) {
01327 dir = INVALID_TRACKDIR;
01328 }
01329
01330 return dir;
01331 }
01332
01340 static bool CanBuildTramTrackOnTile(CompanyID c, TileIndex t, RoadBits r)
01341 {
01342
01343 CompanyID original_company = _current_company;
01344 _current_company = c;
01345
01346 CommandCost ret = DoCommand(t, ROADTYPE_TRAM << 4 | r, 0, DC_NONE, CMD_BUILD_ROAD);
01347
01348 _current_company = original_company;
01349 return CmdSucceeded(ret);
01350 }
01351
01352 static bool IndividualRoadVehicleController(Vehicle *v, const Vehicle *prev)
01353 {
01354 if (v->u.road.overtaking != 0) {
01355 if (IsTileType(v->tile, MP_STATION)) {
01356
01357 v->u.road.overtaking = 0;
01358 } else if (++v->u.road.overtaking_ctr >= 35) {
01359
01360
01361
01362 if (v->u.road.state < RVSB_IN_ROAD_STOP && IsStraightRoadTrackdir((Trackdir)v->u.road.state)) {
01363 v->u.road.overtaking = 0;
01364 }
01365 }
01366 }
01367
01368
01369
01370
01371 if (v->IsInDepot()) return true;
01372
01373 if (v->u.road.state == RVSB_WORMHOLE) {
01374
01375 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
01376
01377 if (IsRoadVehFront(v)) {
01378 const Vehicle *u = RoadVehFindCloseTo(v, gp.x, gp.y, v->direction);
01379 if (u != NULL) {
01380 v->cur_speed = u->First()->cur_speed;
01381 return false;
01382 }
01383 }
01384
01385 if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
01386
01387 v->UpdateDeltaXY(v->direction);
01388 SetRoadVehPosition(v, gp.x, gp.y);
01389 return true;
01390 }
01391
01392 v->x_pos = gp.x;
01393 v->y_pos = gp.y;
01394 VehicleMove(v, !(v->vehstatus & VS_HIDDEN));
01395 return true;
01396 }
01397
01398
01399
01400
01401 RoadDriveEntry rd = _road_drive_data[v->u.road.roadtype][(
01402 (HasBit(v->u.road.state, RVS_IN_DT_ROAD_STOP) ? v->u.road.state & RVSB_ROAD_STOP_TRACKDIR_MASK : v->u.road.state) +
01403 (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->u.road.overtaking][v->u.road.frame + 1];
01404
01405 if (rd.x & RDE_NEXT_TILE) {
01406 TileIndex tile = v->tile + TileOffsByDiagDir((DiagDirection)(rd.x & 3));
01407 Trackdir dir;
01408
01409 if (IsRoadVehFront(v)) {
01410
01411 dir = RoadFindPathToDest(v, tile, (DiagDirection)(rd.x & 3));
01412 } else {
01413 dir = FollowPreviousRoadVehicle(v, prev, tile, (DiagDirection)(rd.x & 3), false);
01414 }
01415
01416 if (dir == INVALID_TRACKDIR) {
01417 if (!IsRoadVehFront(v)) error("Disconnecting road vehicle.");
01418 v->cur_speed = 0;
01419 return false;
01420 }
01421
01422 again:
01423 uint start_frame = RVC_DEFAULT_START_FRAME;
01424 if (IsReversingRoadTrackdir(dir)) {
01425
01426 if (v->u.road.roadtype == ROADTYPE_TRAM) {
01427
01428
01429 RoadBits needed;
01430 switch (dir) {
01431 default: NOT_REACHED();
01432 case TRACKDIR_RVREV_NE: needed = ROAD_SW; break;
01433 case TRACKDIR_RVREV_SE: needed = ROAD_NW; break;
01434 case TRACKDIR_RVREV_SW: needed = ROAD_NE; break;
01435 case TRACKDIR_RVREV_NW: needed = ROAD_SE; break;
01436 }
01437 if ((v->Previous() != NULL && v->Previous()->tile == tile) ||
01438 (IsRoadVehFront(v) && IsNormalRoadTile(tile) && !HasRoadWorks(tile) &&
01439 (needed & GetRoadBits(tile, ROADTYPE_TRAM)) != ROAD_NONE)) {
01440
01441
01442
01443
01444
01445
01446
01447
01448
01449
01450 } else if (!IsRoadVehFront(v) || !CanBuildTramTrackOnTile(v->owner, tile, needed) || ((~needed & GetAnyRoadBits(v->tile, ROADTYPE_TRAM, false)) == ROAD_NONE)) {
01451
01452
01453
01454
01455
01456
01457
01458
01459
01460
01461
01462 tile = v->tile;
01463 start_frame = RVC_TURN_AROUND_START_FRAME_SHORT_TRAM;
01464 } else {
01465
01466 v->cur_speed = 0;
01467 return false;
01468 }
01469 } else if (IsNormalRoadTile(v->tile) && GetDisallowedRoadDirections(v->tile) != DRD_NONE) {
01470 v->cur_speed = 0;
01471 return false;
01472 } else {
01473 tile = v->tile;
01474 }
01475 }
01476
01477
01478 const RoadDriveEntry *rdp = _road_drive_data[v->u.road.roadtype][(dir + (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->u.road.overtaking];
01479
01480 int x = TileX(tile) * TILE_SIZE + rdp[start_frame].x;
01481 int y = TileY(tile) * TILE_SIZE + rdp[start_frame].y;
01482
01483 Direction new_dir = RoadVehGetSlidingDirection(v, x, y);
01484 if (IsRoadVehFront(v)) {
01485 Vehicle *u = RoadVehFindCloseTo(v, x, y, new_dir);
01486 if (u != NULL) {
01487 v->cur_speed = u->First()->cur_speed;
01488 return false;
01489 }
01490 }
01491
01492 uint32 r = VehicleEnterTile(v, tile, x, y);
01493 if (HasBit(r, VETS_CANNOT_ENTER)) {
01494 if (!IsTileType(tile, MP_TUNNELBRIDGE)) {
01495 v->cur_speed = 0;
01496 return false;
01497 }
01498
01499 dir = _road_reverse_table[rd.x & 3];
01500 goto again;
01501 }
01502
01503 if (IsInsideMM(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) && IsTileType(v->tile, MP_STATION)) {
01504 if (IsReversingRoadTrackdir(dir) && IsInsideMM(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) {
01505
01506
01507 v->cur_speed = 0;
01508 return false;
01509 }
01510 if (IsRoadStop(v->tile)) {
01511 RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile));
01512
01513
01514
01515 if (IsStandardRoadStopTile(v->tile) || HasBit(v->u.road.state, RVS_IS_STOPPING)) {
01516 rs->FreeBay(HasBit(v->u.road.state, RVS_USING_SECOND_BAY));
01517 ClrBit(v->u.road.state, RVS_IS_STOPPING);
01518 }
01519 if (IsStandardRoadStopTile(v->tile)) rs->SetEntranceBusy(false);
01520 }
01521 }
01522
01523 if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
01524 v->tile = tile;
01525 v->u.road.state = (byte)dir;
01526 v->u.road.frame = start_frame;
01527 }
01528 if (new_dir != v->direction) {
01529 v->direction = new_dir;
01530 v->cur_speed -= v->cur_speed >> 2;
01531 }
01532
01533 v->UpdateDeltaXY(v->direction);
01534 RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y));
01535 return true;
01536 }
01537
01538 if (rd.x & RDE_TURNED) {
01539
01540 Trackdir dir;
01541 uint turn_around_start_frame = RVC_TURN_AROUND_START_FRAME;
01542
01543 RoadBits tram;
01544 if (v->u.road.roadtype == ROADTYPE_TRAM && !IsRoadDepotTile(v->tile) && CountBits(tram = GetAnyRoadBits(v->tile, ROADTYPE_TRAM, true)) == 1) {
01545
01546
01547
01548
01549
01550
01551
01552
01553
01554 turn_around_start_frame = RVC_START_FRAME_AFTER_LONG_TRAM;
01555 switch (rd.x & 0x3) {
01556 default: NOT_REACHED();
01557 case DIAGDIR_NW: dir = TRACKDIR_RVREV_SE; break;
01558 case DIAGDIR_NE: dir = TRACKDIR_RVREV_SW; break;
01559 case DIAGDIR_SE: dir = TRACKDIR_RVREV_NW; break;
01560 case DIAGDIR_SW: dir = TRACKDIR_RVREV_NE; break;
01561 }
01562 } else {
01563 if (IsRoadVehFront(v)) {
01564
01565 dir = RoadFindPathToDest(v, v->tile, (DiagDirection)(rd.x & 3));
01566 } else {
01567 dir = FollowPreviousRoadVehicle(v, prev, v->tile, (DiagDirection)(rd.x & 3), true);
01568 }
01569 }
01570
01571 if (dir == INVALID_TRACKDIR) {
01572 v->cur_speed = 0;
01573 return false;
01574 }
01575
01576 const RoadDriveEntry *rdp = _road_drive_data[v->u.road.roadtype][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + dir];
01577
01578 int x = TileX(v->tile) * TILE_SIZE + rdp[turn_around_start_frame].x;
01579 int y = TileY(v->tile) * TILE_SIZE + rdp[turn_around_start_frame].y;
01580
01581 Direction new_dir = RoadVehGetSlidingDirection(v, x, y);
01582 if (IsRoadVehFront(v) && RoadVehFindCloseTo(v, x, y, new_dir) != NULL) return false;
01583
01584 uint32 r = VehicleEnterTile(v, v->tile, x, y);
01585 if (HasBit(r, VETS_CANNOT_ENTER)) {
01586 v->cur_speed = 0;
01587 return false;
01588 }
01589
01590 v->u.road.state = dir;
01591 v->u.road.frame = turn_around_start_frame;
01592
01593 if (new_dir != v->direction) {
01594 v->direction = new_dir;
01595 v->cur_speed -= v->cur_speed >> 2;
01596 }
01597
01598 v->UpdateDeltaXY(v->direction);
01599 RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y));
01600 return true;
01601 }
01602
01603
01604
01605
01606 if (v->Next() != NULL && IsRoadDepotTile(v->tile)) {
01607 if (v->u.road.frame == v->u.road.cached_veh_length + RVC_DEPOT_START_FRAME) {
01608 RoadVehLeaveDepot(v->Next(), false);
01609 }
01610 }
01611
01612
01613 int x = (v->x_pos & ~15) + (rd.x & 15);
01614 int y = (v->y_pos & ~15) + (rd.y & 15);
01615
01616 Direction new_dir = RoadVehGetSlidingDirection(v, x, y);
01617
01618 if (IsRoadVehFront(v) && !IsInsideMM(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) {
01619
01620
01621 Vehicle *u = RoadVehFindCloseTo(v, x, y, new_dir);
01622
01623 if (u != NULL) {
01624 u = u->First();
01625
01626 if (v->u.road.overtaking == 0) RoadVehCheckOvertake(v, u);
01627 if (v->u.road.overtaking == 0) v->cur_speed = u->cur_speed;
01628 return false;
01629 }
01630 }
01631
01632 Direction old_dir = v->direction;
01633 if (new_dir != old_dir) {
01634 v->direction = new_dir;
01635 v->cur_speed -= (v->cur_speed >> 2);
01636 if (old_dir != v->u.road.state) {
01637
01638 v->UpdateDeltaXY(v->direction);
01639 SetRoadVehPosition(v, v->x_pos, v->y_pos);
01640
01641
01642
01643 return true;
01644 }
01645 }
01646
01647
01648
01649
01650
01651
01652 if (IsRoadVehFront(v) && ((IsInsideMM(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END) &&
01653 _road_veh_data_1[v->u.road.state - RVSB_IN_ROAD_STOP + (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)] == v->u.road.frame) ||
01654 (IsInsideMM(v->u.road.state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) &&
01655 v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile)) &&
01656 v->owner == GetTileOwner(v->tile) &&
01657 GetRoadStopType(v->tile) == (IsCargoInClass(v->cargo_type, CC_PASSENGERS) ? ROADSTOP_BUS : ROADSTOP_TRUCK) &&
01658 v->u.road.frame == RVC_DRIVE_THROUGH_STOP_FRAME))) {
01659
01660 RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile));
01661 Station *st = GetStationByTile(v->tile);
01662
01663
01664
01665
01666 if (!v->current_order.IsType(OT_LEAVESTATION)) {
01667
01668
01669 if (IsDriveThroughStopTile(v->tile)) {
01670 TileIndex next_tile = TILE_ADD(v->tile, TileOffsByDir(v->direction));
01671 RoadStopType type = IsCargoInClass(v->cargo_type, CC_PASSENGERS) ? ROADSTOP_BUS : ROADSTOP_TRUCK;
01672
01673
01674 if (IsDriveThroughStopTile(next_tile) && (GetRoadStopType(next_tile) == type) && GetStationIndex(v->tile) == GetStationIndex(next_tile)) {
01675 RoadStop *rs_n = GetRoadStopByTile(next_tile, type);
01676
01677 if (rs_n->IsFreeBay(HasBit(v->u.road.state, RVS_USING_SECOND_BAY)) && rs_n->num_vehicles < RoadStop::MAX_VEHICLES) {
01678
01679 ClearSlot(v);
01680 rs_n->num_vehicles++;
01681 v->u.road.slot = rs_n;
01682 v->dest_tile = rs_n->xy;
01683 v->u.road.slot_age = 14;
01684
01685 v->u.road.frame++;
01686 RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y));
01687 return true;
01688 }
01689 }
01690 }
01691
01692 rs->SetEntranceBusy(false);
01693
01694 v->last_station_visited = st->index;
01695
01696 if (IsDriveThroughStopTile(v->tile) || (v->current_order.IsType(OT_GOTO_STATION) && v->current_order.GetDestination() == st->index)) {
01697 RoadVehArrivesAt(v, st);
01698 v->BeginLoading();
01699 return false;
01700 }
01701 } else {
01702
01703 if (rs->IsEntranceBusy()) {
01704
01705 v->cur_speed = 0;
01706 return false;
01707 }
01708 v->current_order.Free();
01709 ClearSlot(v);
01710 }
01711
01712 if (IsStandardRoadStopTile(v->tile)) rs->SetEntranceBusy(true);
01713
01714 if (rs == v->u.road.slot) {
01715
01716 ClearSlot(v);
01717 } else if (v->u.road.slot != NULL) {
01718
01719
01720
01721 DEBUG(ms, 0, "Vehicle %d (index %d) arrived at wrong stop", v->unitnumber, v->index);
01722 if (v->tile != v->dest_tile) {
01723 DEBUG(ms, 2, " current tile 0x%X is not destination tile 0x%X. Route problem", v->tile, v->dest_tile);
01724 }
01725 if (v->dest_tile != v->u.road.slot->xy) {
01726 DEBUG(ms, 2, " stop tile 0x%X is not destination tile 0x%X. Multistop desync", v->u.road.slot->xy, v->dest_tile);
01727 }
01728 if (!v->current_order.IsType(OT_GOTO_STATION)) {
01729 DEBUG(ms, 2, " current order type (%d) is not OT_GOTO_STATION", v->current_order.GetType());
01730 } else {
01731 if (v->current_order.GetDestination() != st->index)
01732 DEBUG(ms, 2, " current station %d is not target station in current_order.station (%d)",
01733 st->index, v->current_order.GetDestination());
01734 }
01735
01736 DEBUG(ms, 2, " force a slot clearing");
01737 ClearSlot(v);
01738 }
01739
01740 StartRoadVehSound(v);
01741 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01742 }
01743
01744
01745
01746 uint32 r = VehicleEnterTile(v, v->tile, x, y);
01747 if (HasBit(r, VETS_CANNOT_ENTER)) {
01748 v->cur_speed = 0;
01749 return false;
01750 }
01751
01752 if (v->current_order.IsType(OT_LEAVESTATION) && IsDriveThroughStopTile(v->tile)) {
01753 v->current_order.Free();
01754 ClearSlot(v);
01755 }
01756
01757
01758
01759 if (!HasBit(r, VETS_ENTERED_WORMHOLE)) v->u.road.frame++;
01760
01761 v->UpdateDeltaXY(v->direction);
01762 RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y));
01763 return true;
01764 }
01765
01766 static void RoadVehController(Vehicle *v)
01767 {
01768
01769 v->tick_counter++;
01770 v->current_order_time++;
01771 if (v->u.road.reverse_ctr != 0) v->u.road.reverse_ctr--;
01772
01773
01774 if (v->vehstatus & VS_CRASHED) {
01775 RoadVehIsCrashed(v);
01776 return;
01777 }
01778
01779 RoadVehCheckTrainCrash(v);
01780
01781
01782 if (v->breakdown_ctr != 0) {
01783 if (v->breakdown_ctr <= 2) {
01784 HandleBrokenRoadVeh(v);
01785 return;
01786 }
01787 if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
01788 }
01789
01790 if (v->vehstatus & VS_STOPPED) return;
01791
01792 ProcessOrders(v);
01793 v->HandleLoading();
01794
01795 if (v->current_order.IsType(OT_LOADING)) return;
01796
01797 if (v->IsInDepot() && RoadVehLeaveDepot(v, true)) return;
01798
01799
01800 int j = RoadVehAccelerate(v);
01801
01802 int adv_spd = (v->direction & 1) ? 192 : 256;
01803 while (j >= adv_spd) {
01804 j -= adv_spd;
01805
01806 Vehicle *u = v;
01807 for (Vehicle *prev = NULL; u != NULL; prev = u, u = u->Next()) {
01808 if (!IndividualRoadVehicleController(u, prev)) break;
01809 }
01810
01811
01812 adv_spd = (v->direction & 1) ? 192 : 256;
01813
01814
01815 if (j >= adv_spd && RoadVehCheckTrainCrash(v)) break;
01816 }
01817
01818 for (Vehicle *u = v; u != NULL; u = u->Next()) {
01819 if ((u->vehstatus & VS_HIDDEN) != 0) continue;
01820
01821 uint16 old_image = u->cur_image;
01822 u->cur_image = u->GetImage(u->direction);
01823 if (old_image != u->cur_image) VehicleMove(u, true);
01824 }
01825
01826 if (v->progress == 0) v->progress = j;
01827 }
01828
01829 static void AgeRoadVehCargo(Vehicle *v)
01830 {
01831 if (_age_cargo_skip_counter != 0) return;
01832 v->cargo.AgeCargo();
01833 }
01834
01835 void RoadVehicle::Tick()
01836 {
01837 AgeRoadVehCargo(this);
01838
01839 if (IsRoadVehFront(this)) {
01840 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01841 RoadVehController(this);
01842 }
01843 }
01844
01845 static void CheckIfRoadVehNeedsService(Vehicle *v)
01846 {
01847
01848 if (v->u.road.slot != NULL || _settings_game.vehicle.servint_roadveh == 0 || !v->NeedsAutomaticServicing()) return;
01849 if (v->IsInDepot()) {
01850 VehicleServiceInDepot(v);
01851 return;
01852 }
01853
01854
01855 const Depot *depot = FindClosestRoadDepot(v);
01856
01857 if (depot == NULL || DistanceManhattan(v->tile, depot->xy) > 12) {
01858 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01859 v->current_order.MakeDummy();
01860 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01861 }
01862 return;
01863 }
01864
01865 if (v->current_order.IsType(OT_GOTO_DEPOT) &&
01866 v->current_order.GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS &&
01867 !Chance16(1, 20)) {
01868 return;
01869 }
01870
01871 if (v->current_order.IsType(OT_LOADING)) v->LeaveStation();
01872 ClearSlot(v);
01873
01874 v->current_order.MakeGoToDepot(depot->index, ODTFB_SERVICE);
01875 v->dest_tile = depot->xy;
01876 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01877 }
01878
01879 void RoadVehicle::OnNewDay()
01880 {
01881 if (!IsRoadVehFront(this)) return;
01882
01883 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
01884 if (this->u.road.blocked_ctr == 0) CheckVehicleBreakdown(this);
01885
01886 AgeVehicle(this);
01887 CheckIfRoadVehNeedsService(this);
01888
01889 CheckOrders(this);
01890
01891
01892 if (this->current_order.IsType(OT_GOTO_STATION) && this->u.road.slot != NULL && this->u.road.slot_age-- == 0) {
01893 DEBUG(ms, 3, "Slot expired for vehicle %d (index %d) at stop 0x%X",
01894 this->unitnumber, this->index, this->u.road.slot->xy);
01895 ClearSlot(this);
01896 }
01897
01898
01899 if (!(this->vehstatus & VS_STOPPED) && this->current_order.IsType(OT_GOTO_STATION) && !(this->current_order.GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) && this->u.road.slot == NULL && !(this->vehstatus & VS_CRASHED)) {
01900 Station *st = GetStation(this->current_order.GetDestination());
01901 RoadStop *rs = st->GetPrimaryRoadStop(this);
01902 RoadStop *best = NULL;
01903
01904 if (rs != NULL) {
01905
01906
01907
01908
01909
01910
01911 if (DistanceManhattan(this->tile, rs->xy) < 16 || st->rect.PtInExtendedRect(TileX(this->tile), TileY(this->tile), 2)) {
01912 uint dist, badness;
01913 uint minbadness = UINT_MAX;
01914
01915 DEBUG(ms, 2, "Attempting to obtain a slot for vehicle %d (index %d) at station %d (0x%X)",
01916 this->unitnumber, this->index, st->index, st->xy
01917 );
01918
01919 for (; rs != NULL; rs = rs->GetNextRoadStop(this)) {
01920 if (rs->num_vehicles >= RoadStop::MAX_VEHICLES) {
01921 DEBUG(ms, 4, " stop 0x%X's queue is full, not treating further", rs->xy);
01922 continue;
01923 }
01924 dist = RoadFindPathToStop(this, rs->xy);
01925 if (dist == UINT_MAX) {
01926 DEBUG(ms, 4, " stop 0x%X is unreachable, not treating further", rs->xy);
01927 continue;
01928 }
01929 badness = (rs->num_vehicles + 1) * (rs->num_vehicles + 1) + dist;
01930
01931 DEBUG(ms, 4, " stop 0x%X has %d vehicle%s waiting", rs->xy, rs->num_vehicles, rs->num_vehicles == 1 ? "":"s");
01932 DEBUG(ms, 4, " distance is %u", dist);
01933 DEBUG(ms, 4, " badness %u", badness);
01934
01935 if (badness < minbadness) {
01936 best = rs;
01937 minbadness = badness;
01938 }
01939 }
01940
01941 if (best != NULL) {
01942 best->num_vehicles++;
01943 DEBUG(ms, 3, "Assigned to stop 0x%X", best->xy);
01944
01945 this->u.road.slot = best;
01946 this->dest_tile = best->xy;
01947 this->u.road.slot_age = 14;
01948 } else {
01949 DEBUG(ms, 3, "Could not find a suitable stop");
01950 }
01951 } else {
01952 DEBUG(ms, 5, "Distance from station too far. Postponing slotting for vehicle %d (index %d) at station %d, (0x%X)",
01953 this->unitnumber, this->index, st->index, st->xy);
01954 }
01955 } else {
01956 DEBUG(ms, 4, "No road stop for vehicle %d (index %d) at station %d (0x%X)",
01957 this->unitnumber, this->index, st->index, st->xy);
01958 }
01959 }
01960
01961 if (this->running_ticks == 0) return;
01962
01963 CommandCost cost(EXPENSES_ROADVEH_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
01964
01965 this->profit_this_year -= cost.GetCost();
01966 this->running_ticks = 0;
01967
01968 SubtractMoneyFromCompanyFract(this->owner, cost);
01969
01970 InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
01971 InvalidateWindowClasses(WC_ROADVEH_LIST);
01972 }
01973
01984 CommandCost CmdRefitRoadVeh(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01985 {
01986 Vehicle *v;
01987 CommandCost cost(EXPENSES_ROADVEH_RUN);
01988 CargoID new_cid = GB(p2, 0, 8);
01989 byte new_subtype = GB(p2, 8, 8);
01990 bool only_this = HasBit(p2, 16);
01991 uint16 capacity = CALLBACK_FAILED;
01992 uint total_capacity = 0;
01993
01994 if (!IsValidVehicleID(p1)) return CMD_ERROR;
01995
01996 v = GetVehicle(p1);
01997
01998 if (v->type != VEH_ROAD || !CheckOwnership(v->owner)) return CMD_ERROR;
01999 if (!v->IsStoppedInDepot()) return_cmd_error(STR_9013_MUST_BE_STOPPED_INSIDE);
02000 if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_CAN_T_REFIT_DESTROYED_VEHICLE);
02001
02002 if (new_cid >= NUM_CARGO) return CMD_ERROR;
02003
02004 for (; v != NULL; v = (only_this ? NULL : v->Next())) {
02005
02006
02007
02008 if (!CanRefitTo(v->engine_type, new_cid)) continue;
02009
02010 const Engine *e = GetEngine(v->engine_type);
02011 if (!e->CanCarryCargo()) continue;
02012
02013 if (HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
02014
02015 CargoID temp_cid = v->cargo_type;
02016 byte temp_subtype = v->cargo_subtype;
02017 v->cargo_type = new_cid;
02018 v->cargo_subtype = new_subtype;
02019
02020
02021 capacity = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
02022
02023
02024 v->cargo_type = temp_cid;
02025 v->cargo_subtype = temp_subtype;
02026 }
02027
02028 if (capacity == CALLBACK_FAILED) {
02029
02030
02031 CargoID old_cid = e->GetDefaultCargoType();
02032
02033
02034
02035
02036 capacity = GetVehicleProperty(v, 0x0F, e->u.road.capacity);
02037 switch (old_cid) {
02038 case CT_PASSENGERS: break;
02039 case CT_MAIL:
02040 case CT_GOODS: capacity *= 2; break;
02041 default: capacity *= 4; break;
02042 }
02043 switch (new_cid) {
02044 case CT_PASSENGERS: break;
02045 case CT_MAIL:
02046 case CT_GOODS: capacity /= 2; break;
02047 default: capacity /= 4; break;
02048 }
02049 }
02050
02051 total_capacity += capacity;
02052
02053 if (new_cid != v->cargo_type) {
02054 cost.AddCost(GetRefitCost(v->engine_type));
02055 }
02056
02057 if (flags & DC_EXEC) {
02058 v->cargo_cap = capacity;
02059 v->cargo.Truncate((v->cargo_type == new_cid) ? capacity : 0);
02060 v->cargo_type = new_cid;
02061 v->cargo_subtype = new_subtype;
02062 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
02063 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
02064 InvalidateWindowClassesData(WC_ROADVEH_LIST, 0);
02065 }
02066 }
02067
02068 if (flags & DC_EXEC) RoadVehUpdateCache(GetVehicle(p1)->First());
02069
02070 _returned_refit_capacity = total_capacity;
02071
02072 return cost;
02073 }