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