Mercurial > hg > openttd
changeset 6857:0005a9ed1903 draft
(svn r10097) -Feature: Add support for articulated road vehicles, or callbacks 11 and 17 for
road vehicles for those who prefer the technical explanation.
author | maedhros <maedhros@openttd.org> |
---|---|
date | Mon, 11 Jun 2007 14:00:16 +0000 |
parents | 7d2e01558528 |
children | de19e5b3f7ff |
files | src/articulated_vehicles.cpp src/articulated_vehicles.h src/depot_gui.cpp src/economy.cpp src/lang/english.txt src/main_gui.cpp src/newgrf.cpp src/newgrf_engine.cpp src/player_gui.cpp src/road_cmd.cpp src/roadveh.h src/roadveh_cmd.cpp src/roadveh_gui.cpp src/station_cmd.cpp src/train_cmd.cpp src/tunnelbridge_cmd.cpp src/vehicle.cpp src/vehicle.h src/vehicle_gui.h src/viewport.cpp src/water_cmd.cpp |
diffstat | 21 files changed, 570 insertions(+), 212 deletions(-) [+] |
line wrap: on
line diff
--- a/src/articulated_vehicles.cpp +++ b/src/articulated_vehicles.cpp @@ -4,16 +4,17 @@ #include "stdafx.h" #include "openttd.h" -#include "debug.h" #include "functions.h" #include "command.h" #include "vehicle.h" #include "articulated_vehicles.h" #include "engine.h" #include "train.h" +#include "roadveh.h" #include "newgrf_callbacks.h" #include "newgrf_engine.h" + uint CountArticulatedParts(EngineID engine_type) { if (!HASBIT(EngInfo(engine_type)->callbackmask, CBM_ARTIC_ENGINE)) return 0; @@ -27,7 +28,7 @@ return i - 1; } -void AddArticulatedParts(Vehicle **vl) +void AddArticulatedParts(Vehicle **vl, VehicleType type) { const Vehicle *v = vl[0]; Vehicle *u = vl[0]; @@ -46,9 +47,8 @@ u = u->next; - EngineID engine_type = GB(callback, 0, 7); + EngineID engine_type = GetFirstEngineOfType(type) + GB(callback, 0, 7); bool flip_image = HASBIT(callback, 7); - const RailVehicleInfo *rvi_artic = RailVehInfo(engine_type); /* get common values from first engine */ u->direction = v->direction; @@ -57,28 +57,57 @@ u->x_pos = v->x_pos; u->y_pos = v->y_pos; u->z_pos = v->z_pos; - u->u.rail.track = v->u.rail.track; - u->u.rail.railtype = v->u.rail.railtype; u->build_year = v->build_year; u->vehstatus = v->vehstatus & ~VS_STOPPED; - u->u.rail.first_engine = v->engine_type; - /* get more settings from rail vehicle info */ - u->spritenum = rvi_artic->image_index; - if (flip_image) u->spritenum++; - u->cargo_type = rvi_artic->cargo_type; u->cargo_subtype = 0; - u->cargo_cap = rvi_artic->capacity; u->max_speed = 0; u->max_age = 0; u->engine_type = engine_type; u->value = 0; - u = new (u) Train(); u->subtype = 0; - SetArticulatedPart(u); u->cur_image = 0xAC2; u->random_bits = VehicleRandomBits(); + switch (type) { + default: NOT_REACHED(); + + case VEH_TRAIN: { + const RailVehicleInfo *rvi_artic = RailVehInfo(engine_type); + + u = new (u) Train(); + u->u.rail.track = v->u.rail.track; + u->u.rail.railtype = v->u.rail.railtype; + u->u.rail.first_engine = v->engine_type; + + u->spritenum = rvi_artic->image_index; + u->cargo_type = rvi_artic->cargo_type; + u->cargo_cap = rvi_artic->capacity; + + SetArticulatedPart(u); + } break; + + case VEH_ROAD: { + const RoadVehicleInfo *rvi_artic = RoadVehInfo(engine_type); + + u = new (u) RoadVehicle(); + u->u.road.first_engine = v->engine_type; + u->u.road.cached_veh_length = GetRoadVehLength(u); + u->u.road.state = RVSB_IN_DEPOT; + + u->u.road.roadtype = v->u.road.roadtype; + u->u.road.compatible_roadtypes = v->u.road.compatible_roadtypes; + + u->spritenum = rvi_artic->image_index; + u->cargo_type = rvi_artic->cargo_type; + u->cargo_cap = rvi_artic->capacity; + + SetRoadVehArticPart(u); + } break; + } + + if (flip_image) u->spritenum++; + VehiclePositionChanged(u); } }
--- a/src/articulated_vehicles.h +++ b/src/articulated_vehicles.h @@ -6,6 +6,6 @@ #define ARTICULATED_VEHICLES_H uint CountArticulatedParts(EngineID engine_type); -void AddArticulatedParts(Vehicle **vl); +void AddArticulatedParts(Vehicle **vl, VehicleType type); #endif /* ARTICULATED_VEHICLES_H */
--- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -184,7 +184,7 @@ DrawStringRightAligned(w->widget[DEPOT_WIDGET_MATRIX].right - 1, y + 4, STR_TINY_BLACK, 0); // Draw the counter break; - case VEH_ROAD: DrawRoadVehImage( v, x + 24, sprite_y, WP(w, depot_d).sel); break; + case VEH_ROAD: DrawRoadVehImage( v, x + 24, sprite_y, 1, WP(w, depot_d).sel); break; case VEH_SHIP: DrawShipImage( v, x + 19, sprite_y - 1, WP(w, depot_d).sel); break; case VEH_AIRCRAFT: { const Sprite *spr = GetSprite(GetAircraftImage(v, DIR_W));
--- a/src/economy.cpp +++ b/src/economy.cpp @@ -30,6 +30,7 @@ #include "vehicle_gui.h" #include "ai/ai.h" #include "train.h" +#include "roadveh.h" #include "aircraft.h" #include "newgrf_engine.h" #include "newgrf_sound.h" @@ -340,7 +341,7 @@ if (v->owner == new_player) { switch (v->type) { case VEH_TRAIN: if (IsFrontEngine(v)) num_train++; break; - case VEH_ROAD: num_road++; break; + case VEH_ROAD: if (IsRoadVehFront(v)) num_road++; break; case VEH_SHIP: num_ship++; break; case VEH_AIRCRAFT: if (IsNormalAircraft(v)) num_aircraft++; break; default: break; @@ -361,7 +362,7 @@ if (IsEngineCountable(v)) GetPlayer(new_player)->num_engines[v->engine_type]++; switch (v->type) { case VEH_TRAIN: if (IsFrontEngine(v)) v->unitnumber = ++num_train; break; - case VEH_ROAD: v->unitnumber = ++num_road; break; + case VEH_ROAD: if (IsRoadVehFront(v)) v->unitnumber = ++num_road; break; case VEH_SHIP: v->unitnumber = ++num_ship; break; case VEH_AIRCRAFT: if (IsNormalAircraft(v)) v->unitnumber = ++num_aircraft; break; default: NOT_REACHED();
--- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2776,6 +2776,8 @@ STR_9026_ROAD_VEHICLE_SELECTION :{BLACK}Road vehicle selection list - click on vehicle for information STR_9027_BUILD_THE_HIGHLIGHTED_ROAD :{BLACK}Build the highlighted road vehicle STR_902A_COST_SPEED_RUNNING_COST :{BLACK}Cost: {CURRENCY}{}Speed: {VELOCITY}{}Running Cost: {CURRENCY}/yr{}Capacity: {CARGO} +STR_ARTICULATED_RV_CAPACITY :{BLACK}Capacity: {LTBLUE} +STR_BARE_CARGO :{CARGO} STR_902C_NAME_ROAD_VEHICLE :{WHITE}Name road vehicle STR_902D_CAN_T_NAME_ROAD_VEHICLE :{WHITE}Can't name road vehicle...
--- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -30,6 +30,7 @@ #include "waypoint.h" #include "variables.h" #include "train.h" +#include "roadveh.h" #include "unmovable_map.h" #include "string.h" #include "screenshot.h" @@ -849,7 +850,7 @@ int dis = -1; FOR_ALL_VEHICLES(v) { - if (v->type == VEH_ROAD) CLRBIT(dis, v->owner); + if (v->type == VEH_ROAD && IsRoadVehFront(v)) CLRBIT(dis, v->owner); } PopupMainPlayerToolbMenu(w, 332, 14, dis); }
--- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -4268,8 +4268,8 @@ | (0 << 0x13) // followvehicle | (1 << 0x14) // trams | (0 << 0x15) // enhancetunnels - | (0 << 0x16) // shortrvs - | (0 << 0x17) // articulatedrvs + | (1 << 0x16) // shortrvs + | (1 << 0x17) // articulatedrvs | (1 << 0x1E); // variablerunningcosts }
--- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -507,7 +507,7 @@ switch (variable) { case 0x40: // Get length of consist case 0x41: // Get length of same consecutive wagons - if (v->type != VEH_TRAIN) return 1; + if (!v->HasFront()) return 1; { const Vehicle* u; @@ -830,7 +830,7 @@ res->ResolveReal = &VehicleResolveReal; res->u.vehicle.self = v; - res->u.vehicle.parent = (v != NULL && v->type == VEH_TRAIN) ? GetFirstVehicleInChain(v) : v; + res->u.vehicle.parent = (v != NULL && v->HasFront()) ? GetFirstVehicleInChain(v) : v; res->u.vehicle.self_type = engine_type;
--- a/src/player_gui.cpp +++ b/src/player_gui.cpp @@ -17,6 +17,7 @@ #include "economy.h" #include "network/network.h" #include "variables.h" +#include "roadveh.h" #include "train.h" #include "aircraft.h" #include "date.h" @@ -649,7 +650,7 @@ if (v->owner == player) { switch (v->type) { case VEH_TRAIN: if (IsFrontEngine(v)) train++; break; - case VEH_ROAD: road++; break; + case VEH_ROAD: if (IsRoadVehFront(v)) road++; break; case VEH_AIRCRAFT: if (IsNormalAircraft(v)) air++; break; case VEH_SHIP: ship++; break; default: break;
--- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -13,6 +13,7 @@ #include "table/sprites.h" #include "table/strings.h" #include "functions.h" +#include "window.h" #include "map.h" #include "landscape.h" #include "tile.h" @@ -1359,7 +1360,13 @@ if (v->type == VEH_ROAD && v->u.road.frame == 11 && _roadveh_enter_depot_dir[GetRoadDepotDirection(tile)] == v->u.road.state) { - VehicleEnterDepot(v); + v->u.road.state = RVSB_IN_DEPOT; + v->vehstatus |= VS_HIDDEN; + v->direction = ReverseDir(v->direction); + if (v->next == NULL) VehicleEnterDepot(v); + v->tile = tile; + + InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); return VETSB_ENTERED_WORMHOLE; } break;
--- a/src/roadveh.h +++ b/src/roadveh.h @@ -8,6 +8,42 @@ #include "vehicle.h" +enum RoadVehicleSubType { + RVST_FRONT, + RVST_ARTIC_PART, +}; + +static inline bool IsRoadVehFront(const Vehicle *v) +{ + assert(v->type == VEH_ROAD); + return v->subtype == RVST_FRONT; +} + +static inline void SetRoadVehFront(Vehicle *v) +{ + assert(v->type == VEH_ROAD); + v->subtype = RVST_FRONT; +} + +static inline bool IsRoadVehArticPart(const Vehicle *v) +{ + assert(v->type == VEH_ROAD); + return v->subtype == RVST_ARTIC_PART; +} + +static inline void SetRoadVehArticPart(Vehicle *v) +{ + assert(v->type == VEH_ROAD); + v->subtype = RVST_ARTIC_PART; +} + +static inline bool RoadVehHasArticPart(const Vehicle *v) +{ + assert(v->type == VEH_ROAD); + return v->next != NULL && IsRoadVehArticPart(v->next); +} + + static inline bool IsRoadVehInDepot(const Vehicle* v) { assert(v->type == VEH_ROAD); @@ -43,7 +79,12 @@ void UpdateDeltaXY(Direction direction); ExpensesType GetExpenseType(bool income) const { return income ? EXPENSES_ROADVEH_INC : EXPENSES_ROADVEH_RUN; } WindowClass GetVehicleListWindowClass() const { return WC_ROADVEH_LIST; } - bool IsPrimaryVehicle() const { return true; } + bool IsPrimaryVehicle() const { return IsRoadVehFront(this); } + bool HasFront() const { return true; } }; +byte GetRoadVehLength(const Vehicle *v); + +void RoadVehUpdateCache(Vehicle *v); + #endif /* ROADVEH_H */
--- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -27,6 +27,7 @@ #include "tunnel_map.h" #include "bridge_map.h" #include "vehicle_gui.h" +#include "articulated_vehicles.h" #include "newgrf_callbacks.h" #include "newgrf_engine.h" #include "newgrf_text.h" @@ -89,7 +90,7 @@ int image; if (is_custom_sprite(img)) { - image = GetCustomVehicleSprite(v, direction); + image = GetCustomVehicleSprite(v, (Direction)(direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(img))); if (image != 0) return image; img = orig_road_vehicle_info[v->engine_type - ROAD_ENGINES_INDEX].image_index; } @@ -120,6 +121,35 @@ return ((_price.roadveh_base >> 3) * GetEngineProperty(engine_type, 0x11, RoadVehInfo(engine_type)->base_cost)) >> 5; } +byte GetRoadVehLength(const Vehicle *v) +{ + byte length = 8; + + uint16 veh_len = GetVehicleCallback(CBID_TRAIN_VEHICLE_LENGTH, 0, 0, v->engine_type, v); + if (veh_len != CALLBACK_FAILED) { + length -= clamp(veh_len, 0, 7); + } + + return length; +} + +void RoadVehUpdateCache(Vehicle *v) +{ + assert(v->type == VEH_ROAD); + assert(IsRoadVehFront(v)); + + for (Vehicle *u = v; u != NULL; u = u->next) { + /* Update the v->first cache. */ + if (u->first == NULL) u->first = v; + + /* Update the 'first engine' */ + u->u.road.first_engine = (v == u) ? INVALID_ENGINE : v->engine_type; + + /* Update the length of the vehicle. */ + u->u.road.cached_veh_length = GetRoadVehLength(u); + } +} + /** Build a road vehicle. * @param tile tile of depot where road vehicle is built * @param flags operation to perform @@ -147,8 +177,17 @@ if (HASBIT(GetRoadTypes(tile), ROADTYPE_TRAM) != HASBIT(EngInfo(p1)->misc_flags, EF_ROAD_TRAM)) return_cmd_error(STR_DEPOT_WRONG_DEPOT_TYPE); - v = AllocateVehicle(); - if (v == NULL) return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); + uint num_vehicles = 1 + CountArticulatedParts(p1); + + /* Allow for the front and up to 10 articulated parts. */ + Vehicle *vl[11]; + memset(&vl, 0, sizeof(vl)); + + if (!AllocateVehicles(vl, num_vehicles)) { + return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); + } + + v = vl[0]; /* find the first free roadveh id */ unit_num = HASBIT(p2, 0) ? 0 : GetFreeUnitNumber(VEH_ROAD); @@ -162,7 +201,7 @@ const RoadVehicleInfo *rvi = RoadVehInfo(p1); v->unitnumber = unit_num; - v->direction = INVALID_DIR; + v->direction = DiagDirToDir(GetRoadDepotDirection(tile)); v->owner = _current_player; v->tile = tile; @@ -193,9 +232,6 @@ v->max_speed = rvi->max_speed; v->engine_type = (byte)p1; - v->u.road.roadtype = HASBIT(EngInfo(v->engine_type)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD; - v->u.road.compatible_roadtypes = RoadTypeToRoadTypes(v->u.road.roadtype); - e = GetEngine(p1); v->reliability = e->reliability; v->reliability_spd_dec = e->reliability_spd_dec; @@ -212,12 +248,20 @@ v = new (v) RoadVehicle(); v->cur_image = 0xC15; v->random_bits = VehicleRandomBits(); + SetRoadVehFront(v); + + v->u.road.roadtype = HASBIT(EngInfo(v->engine_type)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD; + v->u.road.compatible_roadtypes = RoadTypeToRoadTypes(v->u.road.roadtype); + v->u.road.cached_veh_length = GetRoadVehLength(v); v->vehicle_flags = 0; if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SETBIT(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE); + v->first = NULL; v->cargo_cap = GetVehicleProperty(v, 0x0F, rvi->capacity); + AddArticulatedParts(vl, VEH_ROAD); + VehiclePositionChanged(v); InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); @@ -285,6 +329,18 @@ DEBUG(ms, 3, "Clearing slot at 0x%X", rs->xy); } +static bool CheckRoadVehInDepotStopped(const Vehicle *v) +{ + TileIndex tile = v->tile; + + if (!IsTileDepotType(tile, TRANSPORT_ROAD) || !(v->vehstatus & VS_STOPPED)) return false; + + for (; v != NULL; v = v->next) { + if (v->u.road.state != RVSB_IN_DEPOT || v->tile != tile) return false; + } + return true; +} + /** Sell a road vehicle. * @param tile unused * @param flags operation to perform @@ -303,7 +359,7 @@ SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); - if (!IsRoadVehInDepotStopped(v)) { + if (!CheckRoadVehInDepotStopped(v)) { return_cmd_error(STR_9013_MUST_BE_STOPPED_INSIDE); } @@ -536,8 +592,12 @@ rs->FreeBay(HASBIT(v->u.road.state, RVS_USING_SECOND_BAY)); } -static void RoadVehDelete(Vehicle *v) +static void DeleteLastRoadVeh(Vehicle *v) { + Vehicle *u = v; + for (; v->next != NULL; v = v->next) u = v; + u->next = NULL; + DeleteWindowById(WC_VEHICLE_VIEW, v->index); RebuildVehicleLists(); @@ -574,13 +634,15 @@ DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT }; - uint32 r = Random(); + do { + uint32 r = Random(); - v->direction = ChangeDir(v->direction, delta[r & 3]); - BeginVehicleMove(v); - v->UpdateDeltaXY(v->direction); - v->cur_image = GetRoadVehImage(v, v->direction); - SetRoadVehPosition(v, v->x_pos, v->y_pos); + v->direction = ChangeDir(v->direction, delta[r & 3]); + BeginVehicleMove(v); + v->UpdateDeltaXY(v->direction); + v->cur_image = GetRoadVehImage(v, v->direction); + SetRoadVehPosition(v, v->x_pos, v->y_pos); + } while ((v = v->next) != NULL); } static void RoadVehIsCrashed(Vehicle *v) @@ -590,8 +652,8 @@ CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE); } else if (v->u.road.crashed_ctr <= 45) { if ((v->tick_counter & 7) == 0) RoadVehSetRandomDirection(v); - } else if (v->u.road.crashed_ctr >= 2220) { - RoadVehDelete(v); + } else if (v->u.road.crashed_ctr >= 2220 && !(v->tick_counter & 0x1F)) { + DeleteLastRoadVeh(v); } } @@ -609,18 +671,22 @@ static void RoadVehCrash(Vehicle *v) { - uint16 pass; + uint16 pass = 1; v->u.road.crashed_ctr++; - v->vehstatus |= VS_CRASHED; + + for (Vehicle *u = v; u != NULL; u = u->next) { + if (IsCargoInClass(u->cargo_type, CC_PASSENGERS)) pass += u->cargo_count; + + u->vehstatus |= VS_CRASHED; + + MarkAllViewportsDirty(u->left_coord, u->top_coord, u->right_coord + 1, u->bottom_coord + 1); + } + ClearSlot(v); InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); - pass = 1; - if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo_count; - v->cargo_count = 0; - SetDParam(0, pass); AddNewsItem( (pass == 1) ? @@ -636,16 +702,18 @@ static void RoadVehCheckTrainCrash(Vehicle *v) { - TileIndex tile; + for (Vehicle *u = v; u != NULL; u = u->next) { + if (u->u.road.state == RVSB_WORMHOLE) continue; - if (v->u.road.state == RVSB_WORMHOLE) return; + TileIndex tile = u->tile; - tile = v->tile; + if (!IsLevelCrossingTile(tile)) continue; - if (!IsLevelCrossingTile(tile)) return; - - if (VehicleFromPos(tile, v, EnumCheckRoadVehCrashTrain) != NULL) - RoadVehCrash(v); + if (VehicleFromPos(tile, u, EnumCheckRoadVehCrashTrain) != NULL) { + RoadVehCrash(v); + return; + } + } } static void HandleBrokenRoadVeh(Vehicle *v) @@ -798,11 +866,11 @@ short y_diff = v->y_pos - rvf->y; return - rvf->veh != v && v->type == VEH_ROAD && !IsRoadVehInDepot(v) && myabs(v->z_pos - rvf->veh->z_pos) < 6 && v->direction == rvf->dir && + GetFirstVehicleInChain(rvf->veh) != GetFirstVehicleInChain(v) && (dist_x[v->direction] >= 0 || (x_diff > dist_x[v->direction] && x_diff <= 0)) && (dist_x[v->direction] <= 0 || (x_diff < dist_x[v->direction] && x_diff >= 0)) && (dist_y[v->direction] >= 0 || (y_diff > dist_y[v->direction] && y_diff <= 0)) && @@ -972,6 +1040,9 @@ /* Trams can't overtake other trams */ if (v->u.road.roadtype == ROADTYPE_TRAM) return; + /* For now, articulated road vehicles can't overtake anything. */ + if (RoadVehHasArticPart(v)) return; + if (v->direction != u->direction || !(v->direction & 1)) return; /* Check if vehicle is in a road stop, depot, tunnel or bridge or not on a straight road */ @@ -1267,7 +1338,95 @@ 15, 15, 11, 11 }; -static void RoadVehController(Vehicle *v) +static bool RoadVehLeaveDepot(Vehicle *v, bool first) +{ + /* Don't leave if not all the wagons are in the depot. */ + for (const Vehicle *u = v; u != NULL; u = u->next) { + if (u->u.road.state != RVSB_IN_DEPOT || u->tile != v->tile) return false; + } + + DiagDirection dir = GetRoadDepotDirection(v->tile); + v->direction = DiagDirToDir(dir); + + Trackdir tdir = _roadveh_depot_exit_trackdir[dir]; + const RoadDriveEntry *rdp = _road_drive_data[v->u.road.roadtype][(_opt.road_side << RVS_DRIVE_SIDE) + tdir]; + + int x = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF); + int y = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF); + + if (first) { + if (RoadVehFindCloseTo(v, x, y, v->direction) != NULL) return true; + + VehicleServiceInDepot(v); + + StartRoadVehSound(v); + + /* Vehicle is about to leave a depot */ + v->cur_speed = 0; + } + + BeginVehicleMove(v); + + v->vehstatus &= ~VS_HIDDEN; + v->u.road.state = tdir; + v->u.road.frame = RVC_DEPOT_START_FRAME; + + v->cur_image = GetRoadVehImage(v, v->direction); + v->UpdateDeltaXY(v->direction); + SetRoadVehPosition(v,x,y); + + InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); + + return true; +} + +static Trackdir FollowPreviousRoadVehicle(const Vehicle *v, const Vehicle *prev, TileIndex tile, DiagDirection entry_dir) +{ + if (prev->tile == v->tile) { + /* If the previous vehicle is on the same tile as this vehicle is + * then it must have reversed. */ + return _road_reverse_table[entry_dir]; + } + + byte prev_state = prev->u.road.state; + Trackdir dir; + + if (prev_state == RVSB_WORMHOLE || prev_state == RVSB_IN_DEPOT) { + DiagDirection diag_dir = INVALID_DIAGDIR; + + if (IsTunnelTile(tile)) { + diag_dir = GetTunnelDirection(tile); + } else if (IsBridgeTile(tile)) { + diag_dir = GetBridgeRampDirection(tile); + } else if (IsTileType(tile, MP_STREET) && GetRoadTileType(tile) == ROAD_TILE_DEPOT) { + diag_dir = ReverseDiagDir(GetRoadDepotDirection(tile)); + } + + if (diag_dir == INVALID_DIAGDIR) return INVALID_TRACKDIR; + dir = DiagdirToDiagTrackdir(diag_dir); + } else if (HASBIT(prev_state, RVS_IN_DT_ROAD_STOP)) { + dir = (Trackdir)(prev_state & RVSB_ROAD_STOP_TRACKDIR_MASK); + } else if (prev_state < TRACKDIR_END) { + dir = (Trackdir)prev_state; + } else { + return INVALID_TRACKDIR; + } + + /* Do some sanity checking. */ + static const RoadBits required_roadbits[] = { + ROAD_X, ROAD_Y, ROAD_NW | ROAD_NE, ROAD_SW | ROAD_SE, + ROAD_NW | ROAD_SW, ROAD_NE | ROAD_SE, ROAD_X, ROAD_Y + }; + RoadBits required = required_roadbits[dir & 0x07]; + + if ((required & GetAnyRoadBits(tile, v->u.road.roadtype)) == ROAD_NONE) { + dir = INVALID_TRACKDIR; + } + + return dir; +} + +static bool IndividualRoadVehicleController(Vehicle *v, const Vehicle *prev) { Direction new_dir; Direction old_dir; @@ -1275,74 +1434,6 @@ int x,y; uint32 r; - /* decrease counters */ - v->tick_counter++; - if (v->u.road.reverse_ctr != 0) v->u.road.reverse_ctr--; - - /* handle crashed */ - if (v->u.road.crashed_ctr != 0) { - RoadVehIsCrashed(v); - return; - } - - RoadVehCheckTrainCrash(v); - - /* road vehicle has broken down? */ - if (v->breakdown_ctr != 0) { - if (v->breakdown_ctr <= 2) { - HandleBrokenRoadVeh(v); - return; - } - v->breakdown_ctr--; - } - - if (v->vehstatus & VS_STOPPED) return; - - ProcessRoadVehOrder(v); - v->HandleLoading(); - - if (v->current_order.type == OT_LOADING) return; - - if (IsRoadVehInDepot(v)) { - /* Vehicle is about to leave a depot */ - DiagDirection dir; - const RoadDriveEntry* rdp; - Trackdir tdir; - - v->cur_speed = 0; - - dir = GetRoadDepotDirection(v->tile); - v->direction = DiagDirToDir(dir); - - tdir = _roadveh_depot_exit_trackdir[dir]; - rdp = _road_drive_data[v->u.road.roadtype][(_opt.road_side << RVS_DRIVE_SIDE) + tdir]; - - x = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF); - y = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF); - - if (RoadVehFindCloseTo(v, x, y, v->direction) != NULL) return; - - VehicleServiceInDepot(v); - - StartRoadVehSound(v); - - BeginVehicleMove(v); - - v->vehstatus &= ~VS_HIDDEN; - v->u.road.state = tdir; - v->u.road.frame = RVC_DEPOT_START_FRAME; - - v->cur_image = GetRoadVehImage(v, v->direction); - v->UpdateDeltaXY(v->direction); - SetRoadVehPosition(v,x,y); - - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); - return; - } - - /* Check if vehicle needs to proceed, return if it doesn't */ - if (!RoadVehAccelerate(v)) return; - if (v->u.road.overtaking != 0) { if (++v->u.road.overtaking_ctr >= 35) /* If overtaking just aborts at a random moment, we can have a out-of-bound problem, @@ -1353,6 +1444,11 @@ } } + /* If this vehicle is in a depot and we've reached this point it must be + * one of the articulated parts. It will stay in the depot until activated + * by the previous vehicle in the chain when it gets to the right place. */ + if (IsRoadVehInDepot(v)) return true; + /* Save old vehicle position to use at end of move to set viewport area dirty */ BeginVehicleMove(v); @@ -1363,7 +1459,7 @@ const Vehicle *u = RoadVehFindCloseTo(v, gp.x, gp.y, v->direction); if (u != NULL && u->cur_speed < v->cur_speed) { v->cur_speed = u->cur_speed; - return; + return false; } if ((IsTunnelTile(gp.new_tile) || IsBridgeTile(gp.new_tile)) && HASBIT(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) { @@ -1371,14 +1467,14 @@ v->cur_image = GetRoadVehImage(v, v->direction); v->UpdateDeltaXY(v->direction); SetRoadVehPosition(v,gp.x,gp.y); - return; + return true; } v->x_pos = gp.x; v->y_pos = gp.y; VehiclePositionChanged(v); if (!(v->vehstatus & VS_HIDDEN)) EndVehicleMove(v); - return; + return true; } /* Get move position data for next frame. @@ -1390,14 +1486,22 @@ if (rd.x & RDE_NEXT_TILE) { TileIndex tile = v->tile + TileOffsByDiagDir(rd.x & 3); - Trackdir dir = RoadFindPathToDest(v, tile, (DiagDirection)(rd.x & 3)); + Trackdir dir; uint32 r; Direction newdir; const RoadDriveEntry *rdp; + if (IsRoadVehFront(v)) { + /* If this is the front engine, look for the right path. */ + dir = RoadFindPathToDest(v, tile, (DiagDirection)(rd.x & 3)); + } else { + dir = FollowPreviousRoadVehicle(v, prev, tile, (DiagDirection)(rd.x & 3)); + } + if (dir == INVALID_TRACKDIR) { + if (!IsRoadVehFront(v)) error("!Disconnecting road vehicle."); v->cur_speed = 0; - return; + return false; } again: @@ -1415,11 +1519,11 @@ if (!IsTileType(tile, MP_STREET) || GetRoadTileType(tile) != ROAD_TILE_NORMAL || HasRoadWorks(tile) || (needed & GetRoadBits(tile, ROADTYPE_TRAM)) == ROAD_NONE) { /* The tram cannot turn here */ v->cur_speed = 0; - return; + return false; } } else if (IsTileType(v->tile, MP_STREET) && GetRoadTileType(v->tile) == ROAD_TILE_NORMAL && GetDisallowedRoadDirections(v->tile) != DRD_NONE) { v->cur_speed = 0; - return; + return false; } else { tile = v->tile; } @@ -1432,13 +1536,13 @@ y = TileY(tile) * TILE_SIZE + rdp[RVC_DEFAULT_START_FRAME].y; newdir = RoadVehGetSlidingDirection(v, x, y); - if (RoadVehFindCloseTo(v, x, y, newdir) != NULL) return; + if (IsRoadVehFront(v) && RoadVehFindCloseTo(v, x, y, newdir) != NULL) return false; r = VehicleEnterTile(v, tile, x, y); if (HASBIT(r, VETS_CANNOT_ENTER)) { if (!IsTileType(tile, MP_TUNNELBRIDGE)) { v->cur_speed = 0; - return; + return false; } /* Try an about turn to re-enter the previous tile */ dir = _road_reverse_table[rd.x & 3]; @@ -1450,7 +1554,7 @@ /* New direction is trying to turn vehicle around. * We can't turn at the exit of a road stop so wait.*/ v->cur_speed = 0; - return; + return false; } if (IsRoadStop(v->tile)) { RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile)); @@ -1478,7 +1582,7 @@ v->cur_image = GetRoadVehImage(v, newdir); v->UpdateDeltaXY(v->direction); RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y)); - return; + return true; } if (rd.x & RDE_TURNED) { @@ -1490,7 +1594,7 @@ if (dir == INVALID_TRACKDIR) { v->cur_speed = 0; - return; + return false; } rdp = _road_drive_data[v->u.road.roadtype][(_opt.road_side << RVS_DRIVE_SIDE) + dir]; @@ -1499,12 +1603,12 @@ y = TileY(v->tile) * TILE_SIZE + rdp[RVC_TURN_AROUND_START_FRAME].y; newdir = RoadVehGetSlidingDirection(v, x, y); - if (RoadVehFindCloseTo(v, x, y, newdir) != NULL) return; + if (IsRoadVehFront(v) && RoadVehFindCloseTo(v, x, y, newdir) != NULL) return false; r = VehicleEnterTile(v, v->tile, x, y); if (HASBIT(r, VETS_CANNOT_ENTER)) { v->cur_speed = 0; - return; + return false; } v->u.road.state = dir; @@ -1518,7 +1622,18 @@ v->cur_image = GetRoadVehImage(v, newdir); v->UpdateDeltaXY(v->direction); RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y)); - return; + return true; + } + + /* This vehicle is not in a wormhole and it hasn't entered a new tile. If + * it's on a depot tile, check if it's time to activate the next vehicle in + * the chain yet. */ + if (v->next != NULL && + IsTileType(v->tile, MP_STREET) && GetRoadTileType(v->tile) == ROAD_TILE_DEPOT) { + + if (v->u.road.frame == v->u.road.cached_veh_length + RVC_DEPOT_START_FRAME) { + RoadVehLeaveDepot(v->next, false); + } } /* Calculate new position for the vehicle */ @@ -1527,7 +1642,7 @@ new_dir = RoadVehGetSlidingDirection(v, x, y); - if (!IS_BYTE_INSIDE(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) { + if (IsRoadVehFront(v) && !IS_BYTE_INSIDE(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) { /* Vehicle is not in a road stop. * Check for another vehicle to overtake */ Vehicle* u = RoadVehFindCloseTo(v, x, y, new_dir); @@ -1536,7 +1651,7 @@ v->cur_speed = u->cur_speed; /* There is a vehicle in front overtake it if possible */ if (v->u.road.overtaking == 0) RoadVehCheckOvertake(v, u); - return; + return false; } } @@ -1552,7 +1667,7 @@ /* Note, return here means that the frame counter is not incremented * for vehicles changing direction in a road stop. This causes frames to * be repeated. (XXX) Is this intended? */ - return; + return true; } } @@ -1561,12 +1676,12 @@ * and it's the correct type of stop (bus or truck) and the frame equals the stop frame... * (the station test and stop type test ensure that other vehicles, using the road stop as * a through route, do not stop) */ - if ((IS_BYTE_INSIDE(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END) && + if (IsRoadVehFront(v) && ((IS_BYTE_INSIDE(v->u.road.state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END) && _road_veh_data_1[v->u.road.state - RVSB_IN_ROAD_STOP + (_opt.road_side << RVS_DRIVE_SIDE)] == v->u.road.frame) || (IS_BYTE_INSIDE(v->u.road.state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) && v->current_order.dest == GetStationIndex(v->tile) && GetRoadStopType(v->tile) == (IsCargoInClass(v->cargo_type, CC_PASSENGERS) ? RoadStop::BUS : RoadStop::TRUCK) && - v->u.road.frame == RVC_DRIVE_THROUGH_STOP_FRAME)) { + v->u.road.frame == RVC_DRIVE_THROUGH_STOP_FRAME))) { RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile)); Station* st = GetStationByTile(v->tile); @@ -1596,7 +1711,7 @@ v->u.road.frame++; RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y)); - return; + return true; } } } @@ -1608,7 +1723,7 @@ RoadVehArrivesAt(v, st); v->BeginLoading(); - return; + return false; } /* Vehicle is ready to leave a bay in a road stop */ @@ -1616,7 +1731,7 @@ if (rs->IsEntranceBusy()) { /* Road stop entrance is busy, so wait as there is nowhere else to go */ v->cur_speed = 0; - return; + return false; } v->current_order.Free(); ClearSlot(v); @@ -1659,7 +1774,7 @@ r = VehicleEnterTile(v, v->tile, x, y); if (HASBIT(r, VETS_CANNOT_ENTER)) { v->cur_speed = 0; - return; + return false; } /* Move to next frame unless vehicle arrived at a stop position @@ -1669,6 +1784,47 @@ v->cur_image = GetRoadVehImage(v, v->direction); v->UpdateDeltaXY(v->direction); RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y)); + return true; +} + +static void RoadVehController(Vehicle *v) +{ + /* decrease counters */ + v->tick_counter++; + if (v->u.road.reverse_ctr != 0) v->u.road.reverse_ctr--; + + /* handle crashed */ + if (v->u.road.crashed_ctr != 0) { + RoadVehIsCrashed(v); + return; + } + + RoadVehCheckTrainCrash(v); + + /* road vehicle has broken down? */ + if (v->breakdown_ctr != 0) { + if (v->breakdown_ctr <= 2) { + HandleBrokenRoadVeh(v); + return; + } + v->breakdown_ctr--; + } + + if (v->vehstatus & VS_STOPPED) return; + + ProcessRoadVehOrder(v); + v->HandleLoading(); + + if (v->current_order.type == OT_LOADING) return; + + if (IsRoadVehInDepot(v) && RoadVehLeaveDepot(v, true)) return; + + /* Check if vehicle needs to proceed, return if it doesn't */ + if (!RoadVehAccelerate(v)) return; + + for (Vehicle *prev = NULL; v != NULL; prev = v, v = v->next) { + if (!IndividualRoadVehicleController(v, prev)) break; + } } static void AgeRoadVehCargo(Vehicle *v) @@ -1680,7 +1836,8 @@ void RoadVeh_Tick(Vehicle *v) { AgeRoadVehCargo(v); - RoadVehController(v); + + if (IsRoadVehFront(v)) RoadVehController(v); } static void CheckIfRoadVehNeedsService(Vehicle *v) @@ -1738,6 +1895,8 @@ { int32 cost; + if (!IsRoadVehFront(v)) return; + if ((++v->day_counter & 7) == 0) DecreaseVehicleValue(v); if (v->u.road.blocked_ctr == 0) CheckVehicleBreakdown(v); @@ -1862,7 +2021,7 @@ v = GetVehicle(p1); if (v->type != VEH_ROAD || !CheckOwnership(v->owner)) return CMD_ERROR; - if (!IsRoadVehInDepotStopped(v)) return_cmd_error(STR_9013_MUST_BE_STOPPED_INSIDE); + if (!CheckRoadVehInDepotStopped(v)) return_cmd_error(STR_9013_MUST_BE_STOPPED_INSIDE); if (new_cid >= NUM_CARGO || !CanRefitTo(v->engine_type, new_cid)) return CMD_ERROR;
--- a/src/roadveh_gui.cpp +++ b/src/roadveh_gui.cpp @@ -11,6 +11,7 @@ #include "table/strings.h" #include "window.h" #include "gui.h" +#include "strings.h" #include "vehicle.h" #include "viewport.h" #include "command.h" @@ -18,22 +19,61 @@ #include "vehicle_gui.h" #include "newgrf_engine.h" -void DrawRoadVehImage(const Vehicle *v, int x, int y, VehicleID selection) +static inline int RoadVehLengthToPixels(int length) +{ + return (length * 28) / 8; +} + +void DrawRoadVehImage(const Vehicle *v, int x, int y, int count, VehicleID selection) { - SpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v); - DrawSprite(GetRoadVehImage(v, DIR_W), pal, x + 14, y + 6); + int dx = 0; + + /* Road vehicle lengths are measured in eighths of the standard length, so + * count is the number of standard vehicles that should be drawn. If it is + * 0, we draw enough vehicles for 10 standard vehicle lengths. */ + int max_length = (count == 0) ? 80 : count * 8; + + do { + int length = v->u.road.cached_veh_length; - if (v->index == selection) { - DrawFrameRect(x - 1, y - 1, x + 28, y + 12, 15, FR_BORDERONLY); - } + if (dx + length > 0 && dx <= max_length) { + SpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v); + DrawSprite(GetRoadVehImage(v, DIR_W), pal, x + 14 + RoadVehLengthToPixels(dx), y + 6); + + if (v->index == selection) { + DrawFrameRect(x - 1, y - 1, x + 28, y + 12, 15, FR_BORDERONLY); + } + } + + dx += length; + v = v->next; + } while (v != NULL && dx < max_length); } static void RoadVehDetailsWndProc(Window *w, WindowEvent *e) { switch (e->event) { + case WE_CREATE: { + const Vehicle *v = GetVehicle(w->window_number); + + if (!RoadVehHasArticPart(v)) break; + + /* Draw the text under the vehicle instead of next to it, minus the + * height already allocated for the cargo of the first vehicle. */ + uint height_extension = 15 - 11; + + /* Add space for the cargo amount for each part. */ + do { + height_extension += 11; + } while ((v = v->next) != NULL); + + ResizeWindow(w, 0, height_extension); + } break; + case WE_PAINT: { const Vehicle *v = GetVehicle(w->window_number); StringID str; + uint y_offset = RoadVehHasArticPart(v) ? 15 :0; SetWindowWidgetDisabledState(w, 2, v->owner != _local_player); /* disable service-scroller when interval is set to disabled */ @@ -76,37 +116,81 @@ DrawString(2, 45, STR_9010_RELIABILITY_BREAKDOWNS, 0); } - /* Draw service interval text */ - { - SetDParam(0, v->service_interval); - SetDParam(1, v->date_of_last_service); - DrawString(13, 102, _patches.servint_ispercent?STR_SERVICING_INTERVAL_PERCENT:STR_883C_SERVICING_INTERVAL_DAYS, 0); - } - - DrawRoadVehImage(v, 3, 57, INVALID_VEHICLE); + DrawRoadVehImage(v, 3, 57, 0, INVALID_VEHICLE); SetDParam(0, GetCustomEngineName(v->engine_type)); SetDParam(1, v->build_year); SetDParam(2, v->value); - DrawString(34, 57, STR_9011_BUILT_VALUE, 0); + DrawString(34, 57 + y_offset, STR_9011_BUILT_VALUE, 0); + + if (RoadVehHasArticPart(v)) { + AcceptedCargo max_cargo; + char capacity[512]; + + memset(max_cargo, 0, sizeof(max_cargo)); + + for (const Vehicle *u = v; u != NULL; u = u->next) { + max_cargo[u->cargo_type] += u->cargo_cap; + } + + GetString(capacity, STR_ARTICULATED_RV_CAPACITY, lastof(capacity)); - SetDParam(0, v->cargo_type); - SetDParam(1, v->cargo_cap); - DrawString(34, 67, STR_9012_CAPACITY, 0); + bool first = true; + for (CargoID i = 0; i < NUM_CARGO; i++) { + if (max_cargo[i] > 0) { + char buffer[128]; + + SetDParam(0, i); + SetDParam(1, max_cargo[i]); + GetString(buffer, STR_BARE_CARGO, lastof(buffer)); + + if (!first) strecat(capacity, ", ", lastof(capacity)); + strecat(capacity, buffer, lastof(capacity)); + } + } - str = STR_8812_EMPTY; - if (v->cargo_count != 0) { + SetDParamStr(0, capacity); + DrawStringTruncated(34, 67 + y_offset, STR_JUST_STRING, 0, w->width - 34); + + for (const Vehicle *u = v; u != NULL; u = u->next) { + str = STR_8812_EMPTY; + if (u->cargo_count != 0) { + SetDParam(0, u->cargo_type); + SetDParam(1, u->cargo_count); + SetDParam(2, u->cargo_source); + str = STR_8813_FROM; + } + DrawString(34, 78 + y_offset, str, 0); + + y_offset += 11; + } + + y_offset -= 11; + } else { SetDParam(0, v->cargo_type); - SetDParam(1, v->cargo_count); - SetDParam(2, v->cargo_source); - str = STR_8813_FROM; + SetDParam(1, v->cargo_cap); + DrawString(34, 67 + y_offset, STR_9012_CAPACITY, 0); + + str = STR_8812_EMPTY; + if (v->cargo_count != 0) { + SetDParam(0, v->cargo_type); + SetDParam(1, v->cargo_count); + SetDParam(2, v->cargo_source); + str = STR_8813_FROM; + } + DrawString(34, 78 + y_offset, str, 0); } - DrawString(34, 78, str, 0); /* Draw Transfer credits text */ SetDParam(0, v->cargo_feeder_share); - DrawString(34, 89, STR_FEEDER_CARGO_VALUE, 0); + DrawString(34, 90 + y_offset, STR_FEEDER_CARGO_VALUE, 0); + /* Draw service interval text */ + { + SetDParam(0, v->service_interval); + SetDParam(1, v->date_of_last_service); + DrawString(13, 102 + y_offset, _patches.servint_ispercent ? STR_SERVICING_INTERVAL_PERCENT : STR_883C_SERVICING_INTERVAL_DAYS, 0); + } } break; case WE_CLICK: { @@ -151,10 +235,10 @@ { WWT_CAPTION, RESIZE_NONE, 14, 11, 339, 0, 13, STR_900C_DETAILS, STR_018C_WINDOW_TITLE_DRAG_THIS}, { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 340, 379, 0, 13, STR_01AA_NAME, STR_902E_NAME_ROAD_VEHICLE}, { WWT_PANEL, RESIZE_NONE, 14, 0, 379, 14, 55, 0x0, STR_NULL}, -{ WWT_PANEL, RESIZE_NONE, 14, 0, 379, 56, 100, 0x0, STR_NULL}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 10, 101, 106, STR_0188, STR_884D_INCREASE_SERVICING_INTERVAL}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 10, 107, 112, STR_0189, STR_884E_DECREASE_SERVICING_INTERVAL}, -{ WWT_PANEL, RESIZE_NONE, 14, 11, 379, 101, 112, 0x0, STR_NULL}, +{ WWT_PANEL, RESIZE_BOTTOM, 14, 0, 379, 56, 100, 0x0, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 10, 101, 106, STR_0188, STR_884D_INCREASE_SERVICING_INTERVAL}, +{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 10, 107, 112, STR_0189, STR_884E_DECREASE_SERVICING_INTERVAL}, +{ WWT_PANEL, RESIZE_TB, 14, 11, 379, 101, 112, 0x0, STR_NULL}, { WIDGETS_END}, };
--- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -30,6 +30,7 @@ #include "sprite.h" #include "depot.h" #include "train.h" +#include "roadveh.h" #include "water_map.h" #include "industry_map.h" #include "newgrf_callbacks.h" @@ -2280,7 +2281,7 @@ } } else if (v->type == VEH_ROAD) { if (v->u.road.state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)v->u.road.state) && v->u.road.frame == 0) { - if (IsRoadStop(tile)) { + if (IsRoadStop(tile) && IsRoadVehFront(v)) { /* Attempt to allocate a parking bay in a road stop */ RoadStop *rs = GetRoadStopByTile(tile, GetRoadStopType(tile));
--- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -576,7 +576,7 @@ v->group_id = DEFAULT_GROUP; - AddArticulatedParts(vl); + AddArticulatedParts(vl, VEH_TRAIN); _new_vehicle_id = v->index; @@ -755,7 +755,7 @@ vl[0]->u.rail.other_multiheaded_part = vl[1]; vl[1]->u.rail.other_multiheaded_part = vl[0]; } else { - AddArticulatedParts(vl); + AddArticulatedParts(vl, VEH_TRAIN); } TrainConsistChanged(v);
--- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -1427,7 +1427,7 @@ } else if (IsBridge(tile)) { // XXX is this necessary? DiagDirection dir; - if (v->type == VEH_ROAD || (v->type == VEH_TRAIN && IsFrontEngine(v))) { + if (v->HasFront() && v->IsPrimaryVehicle()) { /* modify speed of vehicle */ uint16 spd = _bridge[GetBridgeType(tile)].speed;
--- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -230,11 +230,15 @@ v->first = NULL; if (v->type == VEH_TRAIN) v->u.rail.first_engine = INVALID_ENGINE; + if (v->type == VEH_ROAD) v->u.road.first_engine = INVALID_ENGINE; } FOR_ALL_VEHICLES(v) { - if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) + if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) { TrainConsistChanged(v); + } else if (v->type == VEH_ROAD && IsRoadVehFront(v)) { + RoadVehUpdateCache(v); + } } FOR_ALL_VEHICLES(v) { @@ -498,7 +502,7 @@ { Vehicle *u; - FOR_ALL_VEHICLES(u) if (u->type == VEH_TRAIN && u->next == v) return u; + FOR_ALL_VEHICLES(u) if (u->type == v->type && u->next == v) return u; return NULL; } @@ -531,10 +535,14 @@ Vehicle* u; assert(v != NULL); - assert(v->type == VEH_TRAIN); + assert(v->type == VEH_TRAIN || v->type == VEH_ROAD); if (v->first != NULL) { - if (IsFrontEngine(v->first) || IsFreeWagon(v->first)) return v->first; + if (v->type == VEH_TRAIN) { + if (IsFrontEngine(v->first) || IsFreeWagon(v->first)) return v->first; + } else { + if (IsRoadVehFront(v->first)) return v->first; + } DEBUG(misc, 0, "v->first cache faulty. We shouldn't be here, rebuilding cache!"); } @@ -548,8 +556,10 @@ while ((u = GetPrevVehicleInChain_bruteforce(v)) != NULL) v = u; /* Set the first pointer of all vehicles in that chain to the first wagon */ - if (IsFrontEngine(v) || IsFreeWagon(v)) + if ((v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) || + (v->type == VEH_ROAD && IsRoadVehFront(v))) { for (u = (Vehicle *)v; u != NULL; u = u->next) u->first = (Vehicle *)v; + } return (Vehicle*)v; } @@ -572,9 +582,8 @@ case VEH_TRAIN: return !IsArticulatedPart(v) && // tenders and other articulated parts (!IsMultiheaded(v) || IsTrainEngine(v)); // rear parts of multiheaded engines - case VEH_ROAD: - case VEH_SHIP: - return true; + case VEH_ROAD: return IsRoadVehFront(v); + case VEH_SHIP: return true; default: return false; // Only count player buildable vehicles } } @@ -609,7 +618,9 @@ /* Now remove any artic part. This will trigger an other * destroy vehicle, which on his turn can remove any * other artic parts. */ - if (v->type == VEH_TRAIN && EngineHasArticPart(v)) DeleteVehicle(v->next); + if ((v->type == VEH_TRAIN && EngineHasArticPart(v)) || (v->type == VEH_ROAD && RoadVehHasArticPart(v))) { + DeleteVehicle(v->next); + } } /** @@ -621,7 +632,7 @@ */ void DeleteVehicleChain(Vehicle *v) { - assert(v->type != VEH_TRAIN); + assert(v->type != VEH_TRAIN && v->type != VEH_ROAD); do { Vehicle *u = v; @@ -705,6 +716,7 @@ case VEH_SHIP: if (v->type == VEH_TRAIN && IsTrainWagon(v)) continue; if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue; + if (v->type == VEH_ROAD && !IsRoadVehFront(v)) continue; v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed; /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */ @@ -1876,6 +1888,8 @@ if (w->type == VEH_TRAIN && EngineHasArticPart(w)) { w = GetNextArticPart(w); + } else if (w->type == VEH_ROAD && RoadVehHasArticPart(w)) { + w = w->next; } else { break; } @@ -1886,7 +1900,15 @@ total_cost += GetRefitCost(v->engine_type); } } - } while (v->type == VEH_TRAIN && EngineHasArticPart(v) && (v = GetNextArticPart(v)) != NULL); + + if (v->type == VEH_TRAIN && EngineHasArticPart(v)) { + v = GetNextArticPart(v); + } else if (v->type == VEH_ROAD && RoadVehHasArticPart(v)) { + v = v->next; + } else { + break; + } + } while (v != NULL); if ((flags & DC_EXEC) && v->type == VEH_TRAIN) w = GetNextVehicle(w); } while (v->type == VEH_TRAIN && (v = GetNextVehicle(v)) != NULL); @@ -1964,7 +1986,7 @@ case VEH_ROAD: FOR_ALL_VEHICLES(v) { - if (v->tile == tile && v->type == VEH_ROAD && IsRoadVehInDepot(v)) { + if (v->tile == tile && v->type == VEH_ROAD && IsRoadVehInDepot(v) && IsRoadVehFront(v)) { if (*engine_count == *engine_list_length) ExtendVehicleListSize((const Vehicle***)engine_list, engine_list_length, 25); (*engine_list)[(*engine_count)++] = v; } @@ -2163,7 +2185,7 @@ case VEH_ROAD: InvalidateWindowClasses(WC_ROADVEH_LIST); - v->u.road.state = RVSB_IN_DEPOT; + if (!IsRoadVehFront(v)) v = GetFirstVehicleInChain(v); break; case VEH_SHIP:
--- a/src/vehicle.h +++ b/src/vehicle.h @@ -189,6 +189,8 @@ byte reverse_ctr; struct RoadStop *slot; byte slot_age; + EngineID first_engine; + byte cached_veh_length; RoadType roadtype; RoadTypes compatible_roadtypes;
--- a/src/vehicle_gui.h +++ b/src/vehicle_gui.h @@ -51,7 +51,7 @@ int DrawVehiclePurchaseInfo(int x, int y, uint w, EngineID engine_number); void DrawTrainImage(const Vehicle *v, int x, int y, int count, int skip, VehicleID selection); -void DrawRoadVehImage(const Vehicle *v, int x, int y, VehicleID selection); +void DrawRoadVehImage(const Vehicle *v, int x, int y, int count, VehicleID selection); void DrawShipImage(const Vehicle *v, int x, int y, VehicleID selection); void DrawAircraftImage(const Vehicle *v, int x, int y, VehicleID selection); @@ -75,7 +75,7 @@ { switch (v->type) { case VEH_TRAIN: DrawTrainImage(v, x, y, count, skip, selection); break; - case VEH_ROAD: DrawRoadVehImage(v, x, y, selection); break; + case VEH_ROAD: DrawRoadVehImage(v, x, y, count, selection); break; case VEH_SHIP: DrawShipImage(v, x, y, selection); break; case VEH_AIRCRAFT: DrawAircraftImage(v, x, y, selection); break; default: NOT_REACHED();
--- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -23,6 +23,7 @@ #include "waypoint.h" #include "variables.h" #include "train.h" +#include "roadveh.h" #define VIEWPORT_DRAW_MEM (65536 * 2) @@ -1764,12 +1765,18 @@ ShowTrainViewWindow(v); } +static void SafeShowRoadVehViewWindow(const Vehicle *v) +{ + if (!IsRoadVehFront(v)) v = GetFirstVehicleInChain(v); + ShowRoadVehViewWindow(v); +} + static void Nop(const Vehicle *v) {} typedef void OnVehicleClickProc(const Vehicle *v); static OnVehicleClickProc* const _on_vehicle_click_proc[] = { SafeShowTrainViewWindow, - ShowRoadVehViewWindow, + SafeShowRoadVehViewWindow, ShowShipViewWindow, ShowAircraftViewWindow, Nop, // Special vehicles
--- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -23,6 +23,7 @@ #include "depot.h" #include "vehicle_gui.h" #include "train.h" +#include "roadveh.h" #include "water_map.h" #include "newgrf.h" #include "newgrf_canal.h" @@ -641,21 +642,13 @@ if (!(v->vehstatus & VS_CRASHED)) { uint16 pass = 0; - if (v->type == VEH_ROAD) { // flood bus/truck - pass = 1; // driver - if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo_count; - - v->vehstatus |= VS_CRASHED; - v->u.road.crashed_ctr = 2000; // max 2220, disappear pretty fast - RebuildVehicleLists(); - } else if (v->type == VEH_TRAIN) { + if (v->type == VEH_TRAIN || v->type == VEH_ROAD) { Vehicle *u; v = GetFirstVehicleInChain(v); u = v; - if (IsFrontEngine(v)) pass = 4; // driver - /* crash all wagons, and count passangers */ + /* crash all wagons, and count passengers */ BEGIN_ENUM_WAGONS(v) if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo_count; v->vehstatus |= VS_CRASHED; @@ -663,7 +656,15 @@ END_ENUM_WAGONS(v) v = u; - v->u.rail.crash_anim_pos = 4000; // max 4440, disappear pretty fast + + if (v->type == VEH_TRAIN) { + if (IsFrontEngine(v)) pass += 4; // driver + v->u.rail.crash_anim_pos = 4000; // max 4440, disappear pretty fast + } else { + if (IsRoadVehFront(v)) pass += 1; // driver + v->u.road.crashed_ctr = 2000; // max 2220, disappear pretty fast + } + RebuildVehicleLists(); } else { return;