(svn r10097) -Feature: Add support for articulated road vehicles, or callbacks 11 and 17 for

road vehicles for those who prefer the technical explanation.
This commit is contained in:
maedhros 2007-06-11 14:00:16 +00:00
parent be0f5cf877
commit 3e326085fa
21 changed files with 570 additions and 212 deletions

View File

@ -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 @@ uint CountArticulatedParts(EngineID engine_type)
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 @@ void AddArticulatedParts(Vehicle **vl)
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 @@ void AddArticulatedParts(Vehicle **vl)
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);
}
}

View File

@ -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 */

View File

@ -184,7 +184,7 @@ static void DrawVehicleInDepot(Window *w, const Vehicle *v, int x, int y)
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));

View File

@ -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 @@ void ChangeOwnershipOfPlayerItems(PlayerID old_player, PlayerID new_player)
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 @@ void ChangeOwnershipOfPlayerItems(PlayerID old_player, PlayerID new_player)
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();

View File

@ -2776,6 +2776,8 @@ STR_9025_CENTER_MAIN_VIEW_ON_ROAD :{BLACK}Centre m
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...

View File

@ -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 @@ static void ToolbarRoadClick(Window *w)
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);
}

View File

@ -4268,8 +4268,8 @@ static void InitializeGRFSpecial()
| (0 << 0x13) // followvehicle
| (1 << 0x14) // trams
| (0 << 0x15) // enhancetunnels
| (0 << 0x16) // shortrvs
| (0 << 0x17) // articulatedrvs
| (1 << 0x16) // shortrvs
| (1 << 0x17) // articulatedrvs
| (1 << 0x1E); // variablerunningcosts
}

View File

@ -507,7 +507,7 @@ static uint32 VehicleGetVariable(const ResolverObject *object, byte variable, by
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 @@ static inline void NewVehicleResolver(ResolverObject *res, EngineID engine_type,
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;

View File

@ -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 @@ static void DrawPlayerVehiclesAmount(PlayerID player)
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;

View File

@ -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 @@ static uint32 VehicleEnter_Road(Vehicle *v, TileIndex tile, int x, int y)
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;

View File

@ -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 @@ struct RoadVehicle : public Vehicle {
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 */

View File

@ -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 GetRoadVehImage(const Vehicle* v, Direction direction)
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 @@ static int32 EstimateRoadVehCost(EngineID engine_type)
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 @@ int32 CmdBuildRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
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 @@ int32 CmdBuildRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
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 @@ int32 CmdBuildRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
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 @@ int32 CmdBuildRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
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 @@ void ClearSlot(Vehicle *v)
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 @@ int32 CmdSellRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
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 @@ static void ClearCrashedStation(Vehicle *v)
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 @@ static void RoadVehSetRandomDirection(Vehicle *v)
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 @@ static void RoadVehIsCrashed(Vehicle *v)
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* EnumCheckRoadVehCrashTrain(Vehicle* v, void* data)
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 RoadVehCrash(Vehicle *v)
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 @@ static void* EnumCheckRoadVehClose(Vehicle *v, void* data)
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 @@ static void RoadVehCheckOvertake(Vehicle *v, Vehicle *u)
/* 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 @@ static const byte _road_veh_data_1[] = {
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 @@ static void RoadVehController(Vehicle *v)
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 @@ static void RoadVehController(Vehicle *v)
}
}
/* 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 @@ static void RoadVehController(Vehicle *v)
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 @@ static void RoadVehController(Vehicle *v)
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 @@ static void RoadVehController(Vehicle *v)
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 @@ again:
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 @@ again:
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 @@ again:
/* 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 @@ again:
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 @@ again:
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 @@ again:
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 @@ again:
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 @@ again:
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 @@ again:
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 @@ again:
/* 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 @@ again:
* 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 @@ again:
v->u.road.frame++;
RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y));
return;
return true;
}
}
}
@ -1608,7 +1723,7 @@ again:
RoadVehArrivesAt(v, st);
v->BeginLoading();
return;
return false;
}
/* Vehicle is ready to leave a bay in a road stop */
@ -1616,7 +1731,7 @@ again:
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 @@ again:
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 @@ again:
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 @@ static void AgeRoadVehCargo(Vehicle *v)
void RoadVeh_Tick(Vehicle *v)
{
AgeRoadVehCargo(v);
RoadVehController(v);
if (IsRoadVehFront(v)) RoadVehController(v);
}
static void CheckIfRoadVehNeedsService(Vehicle *v)
@ -1738,6 +1895,8 @@ void OnNewDay_RoadVeh(Vehicle *v)
{
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 @@ int32 CmdRefitRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
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;

View File

@ -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)
{
SpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
DrawSprite(GetRoadVehImage(v, DIR_W), pal, x + 14, y + 6);
return (length * 28) / 8;
}
if (v->index == selection) {
DrawFrameRect(x - 1, y - 1, x + 28, y + 12, 15, FR_BORDERONLY);
}
void DrawRoadVehImage(const Vehicle *v, int x, int y, int count, VehicleID selection)
{
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 (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 @@ static void RoadVehDetailsWndProc(Window *w, WindowEvent *e)
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);
SetDParam(0, v->cargo_type);
SetDParam(1, v->cargo_cap);
DrawString(34, 67, STR_9012_CAPACITY, 0);
if (RoadVehHasArticPart(v)) {
AcceptedCargo max_cargo;
char capacity[512];
str = STR_8812_EMPTY;
if (v->cargo_count != 0) {
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));
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));
}
}
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 @@ static const Widget _roadveh_details_widgets[] = {
{ 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},
};

View File

@ -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 @@ static uint32 VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
}
} 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));

View File

@ -576,7 +576,7 @@ static int32 CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags)
v->group_id = DEFAULT_GROUP;
AddArticulatedParts(vl);
AddArticulatedParts(vl, VEH_TRAIN);
_new_vehicle_id = v->index;
@ -755,7 +755,7 @@ int32 CmdBuildRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
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);

View File

@ -1427,7 +1427,7 @@ static uint32 VehicleEnter_TunnelBridge(Vehicle *v, TileIndex tile, int x, int y
} 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;

View File

@ -230,11 +230,15 @@ void AfterLoadVehicles()
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 @@ static Vehicle *GetPrevVehicleInChain_bruteforce(const Vehicle *v)
{
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 *GetFirstVehicleInChain(const Vehicle *v)
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 @@ Vehicle *GetFirstVehicleInChain(const Vehicle *v)
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 @@ bool IsEngineCountable(const Vehicle *v)
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 @@ void DestroyVehicle(Vehicle *v)
/* 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 DestroyVehicle(Vehicle *v)
*/
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 @@ void CallVehicleTicks()
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 @@ int32 CmdCloneVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
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 @@ int32 CmdCloneVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
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 @@ void BuildDepotVehicleList(VehicleType type, TileIndex tile, Vehicle ***engine_l
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 @@ void VehicleEnterDepot(Vehicle *v)
case VEH_ROAD:
InvalidateWindowClasses(WC_ROADVEH_LIST);
v->u.road.state = RVSB_IN_DEPOT;
if (!IsRoadVehFront(v)) v = GetFirstVehicleInChain(v);
break;
case VEH_SHIP:

View File

@ -189,6 +189,8 @@ struct VehicleRoad {
byte reverse_ctr;
struct RoadStop *slot;
byte slot_age;
EngineID first_engine;
byte cached_veh_length;
RoadType roadtype;
RoadTypes compatible_roadtypes;

View File

@ -51,7 +51,7 @@ void PlayerVehWndProc(Window *w, WindowEvent *e);
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 @@ static inline void DrawVehicleImage(const Vehicle *v, int x, int y, int count, i
{
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();

View File

@ -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 @@ static void SafeShowTrainViewWindow(const Vehicle* v)
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

View File

@ -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 @@ static void FloodVehicle(Vehicle *v)
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 @@ static void FloodVehicle(Vehicle *v)
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;