1
0
Fork 0

(svn r3139) -NewGRF, Feature: support for articulated rail vehicles. This is used, for example, by coal tenders.

release/0.4.5
peter1138 2005-11-05 16:07:26 +00:00
parent d86829d0e0
commit 267314b4d0
6 changed files with 231 additions and 65 deletions

View File

@ -375,6 +375,8 @@ static const SpriteGroup* ResolveVehicleSpriteGroup(const SpriteGroup *spritegro
if (dsg->variable == 0x0C) { if (dsg->variable == 0x0C) {
/* Callback ID */ /* Callback ID */
value = callback_info & 0xFF; value = callback_info & 0xFF;
} else if (dsg->variable == 0x10) {
value = (callback_info >> 8) & 0xFF;
} else if ((dsg->variable >> 6) == 0) { } else if ((dsg->variable >> 6) == 0) {
/* General property */ /* General property */
value = GetDeterministicSpriteValue(dsg->variable); value = GetDeterministicSpriteValue(dsg->variable);

View File

@ -153,6 +153,8 @@ enum CallbackID {
// Refit capacity, the passed vehicle needs to have its ->cargo_type set to // Refit capacity, the passed vehicle needs to have its ->cargo_type set to
// the cargo we are refitting to, returns the new cargo capacity // the cargo we are refitting to, returns the new cargo capacity
CBID_REFIT_CAP = 0x15, CBID_REFIT_CAP = 0x15,
CBID_ARTIC_ENGINE = 0x16,
}; };
// bit positions for rvi->callbackmask, indicates which callbacks are used by an engine // bit positions for rvi->callbackmask, indicates which callbacks are used by an engine
@ -161,6 +163,7 @@ enum CallbackMask {
CBM_WAGON_POWER = 0, CBM_WAGON_POWER = 0,
CBM_VEH_LENGTH = 1, CBM_VEH_LENGTH = 1,
CBM_REFIT_CAP = 3, CBM_REFIT_CAP = 3,
CBM_ARTIC_ENGINE = 4,
}; };
enum { enum {

View File

@ -46,12 +46,17 @@ void TrainCargoChanged(Vehicle *v) {
const RailVehicleInfo *rvi = RailVehInfo(u->engine_type); const RailVehicleInfo *rvi = RailVehInfo(u->engine_type);
uint16 vweight = 0; uint16 vweight = 0;
// vehicle weight is the sum of the weight of the vehicle and the weight of its cargo
vweight += rvi->weight;
vweight += (_cargoc.weights[u->cargo_type] * u->cargo_count) / 16; vweight += (_cargoc.weights[u->cargo_type] * u->cargo_count) / 16;
// powered wagons have extra weight added
if (HASBIT(u->u.rail.flags, VRF_POWEREDWAGON)) // Vehicle weight is not added for articulated parts.
vweight += RailVehInfo(v->engine_type)->pow_wag_weight; if (u->subtype != TS_Artic_Part) {
// vehicle weight is the sum of the weight of the vehicle and the weight of its cargo
vweight += rvi->weight;
// powered wagons have extra weight added
if (HASBIT(u->u.rail.flags, VRF_POWEREDWAGON))
vweight += RailVehInfo(v->engine_type)->pow_wag_weight;
}
// consist weight is the sum of the weight of all vehicles in the consist // consist weight is the sum of the weight of all vehicles in the consist
weight += vweight; weight += vweight;
@ -92,14 +97,11 @@ void TrainConsistChanged(Vehicle *v) {
// update the 'first engine' // update the 'first engine'
u->u.rail.first_engine = (v == u) ? INVALID_VEHICLE : first_engine; u->u.rail.first_engine = (v == u) ? INVALID_VEHICLE : first_engine;
// power is the sum of the powers of all engines and powered wagons in the consist
power += rvi_u->power;
if (rvi_u->visual_effect != 0) { if (rvi_u->visual_effect != 0) {
u->u.rail.cached_vis_effect = rvi_u->visual_effect; u->u.rail.cached_vis_effect = rvi_u->visual_effect;
} else { } else {
if (rvi_u->flags & RVI_WAGON) { if (rvi_u->flags & RVI_WAGON || u->subtype == TS_Artic_Part) {
// Wagons have no effect by default // Wagons and articulated parts have no effect by default
u->u.rail.cached_vis_effect = 0x40; u->u.rail.cached_vis_effect = 0x40;
} else if (rvi_u->engclass == 0) { } else if (rvi_u->engclass == 0) {
// Steam is offset by -4 units // Steam is offset by -4 units
@ -110,28 +112,33 @@ void TrainConsistChanged(Vehicle *v) {
} }
} }
// check if its a powered wagon if (u->subtype != TS_Artic_Part) {
CLRBIT(u->u.rail.flags, VRF_POWEREDWAGON); // power is the sum of the powers of all engines and powered wagons in the consist
if ((rvi_v->pow_wag_power != 0) && (rvi_u->flags & RVI_WAGON) && UsesWagonOverride(u)) { power += rvi_u->power;
if (HASBIT(rvi_u->callbackmask, CBM_WAGON_POWER)) {
uint16 callback = GetCallBackResult(CBID_WAGON_POWER, u->engine_type, u);
if (callback != CALLBACK_FAILED) // check if its a powered wagon
u->u.rail.cached_vis_effect = callback; CLRBIT(u->u.rail.flags, VRF_POWEREDWAGON);
if ((rvi_v->pow_wag_power != 0) && (rvi_u->flags & RVI_WAGON) && UsesWagonOverride(u)) {
if (HASBIT(rvi_u->callbackmask, CBM_WAGON_POWER)) {
uint16 callback = GetCallBackResult(CBID_WAGON_POWER, u->engine_type, u);
if (callback != CALLBACK_FAILED)
u->u.rail.cached_vis_effect = callback;
}
if (u->u.rail.cached_vis_effect < 0x40) {
/* wagon is powered */
SETBIT(u->u.rail.flags, VRF_POWEREDWAGON); // cache 'powered' status
power += rvi_v->pow_wag_power;
}
} }
if (u->u.rail.cached_vis_effect < 0x40) { // max speed is the minimum of the speed limits of all vehicles in the consist
/* wagon is powered */ if (!(rvi_u->flags & RVI_WAGON) || _patches.wagon_speed_limits)
SETBIT(u->u.rail.flags, VRF_POWEREDWAGON); // cache 'powered' status if (rvi_u->max_speed != 0 && !UsesWagonOverride(u))
power += rvi_v->pow_wag_power; max_speed = min(rvi_u->max_speed, max_speed);
}
} }
// max speed is the minimum of the speed limits of all vehicles in the consist
if (!(rvi_u->flags & RVI_WAGON) || _patches.wagon_speed_limits)
if (rvi_u->max_speed != 0 && !UsesWagonOverride(u))
max_speed = min(rvi_u->max_speed, max_speed);
// check the vehicle length (callback) // check the vehicle length (callback)
veh_len = CALLBACK_FAILED; veh_len = CALLBACK_FAILED;
if (HASBIT(rvi_u->callbackmask, CBM_VEH_LENGTH)) if (HASBIT(rvi_u->callbackmask, CBM_VEH_LENGTH))
@ -418,6 +425,78 @@ void DrawTrainEngine(int x, int y, EngineID engine, uint32 image_ormod)
DrawSprite(image | image_ormod, x, y); DrawSprite(image | image_ormod, x, y);
} }
static uint CountArticulatedParts(const RailVehicleInfo *rvi, EngineID engine_type)
{
uint16 callback;
uint i;
if (!HASBIT(rvi->callbackmask, CBM_ARTIC_ENGINE))
return 0;
for (i = 1; i < 10; i++) {
callback = GetCallBackResult(CBID_ARTIC_ENGINE + (i << 8), engine_type, NULL);
if (callback == CALLBACK_FAILED || callback == 0xFF)
break;
}
return i;
}
static void AddArticulatedParts(const RailVehicleInfo *rvi, Vehicle **vl)
{
const RailVehicleInfo *rvi_artic;
EngineID engine_type;
Vehicle *v = vl[0];
Vehicle *u = v;
uint16 callback;
bool flip_image;
uint i;
if (!HASBIT(rvi->callbackmask, CBM_ARTIC_ENGINE))
return;
for (i = 1; i < 10; i++) {
callback = GetCallBackResult(CBID_ARTIC_ENGINE + (i << 8), v->engine_type, NULL);
if (callback == CALLBACK_FAILED || callback == 0xFF)
return;
u->next = vl[i];
u = u->next;
engine_type = GB(callback, 0, 6);
flip_image = HASBIT(callback, 7);
rvi_artic = RailVehInfo(engine_type);
// get common values from first engine
u->direction = v->direction;
u->owner = v->owner;
u->tile = v->tile;
u->x_pos = v->x_pos;
u->y_pos = v->y_pos;
u->z_pos = v->z_pos;
u->z_height = v->z_height;
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_cap = rvi_artic->capacity;
u->max_speed = 0;
u->max_age = 0;
u->engine_type = engine_type;
u->value = 0;
u->type = VEH_Train;
u->subtype = TS_Artic_Part;
u->cur_image = 0xAC2;
VehiclePositionChanged(u);
}
}
static int32 CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags) static int32 CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags)
{ {
@ -426,24 +505,27 @@ static int32 CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags)
const RailVehicleInfo *rvi; const RailVehicleInfo *rvi;
const Engine *e; const Engine *e;
int x,y; int x,y;
uint num_vehicles;
SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
rvi = RailVehInfo(engine); rvi = RailVehInfo(engine);
value = (rvi->base_cost * _price.build_railwagon) >> 8; value = (rvi->base_cost * _price.build_railwagon) >> 8;
if (!(flags & DC_QUERY_COST)) { num_vehicles = 1 + CountArticulatedParts(rvi, engine);
_error_message = STR_00E1_TOO_MANY_VEHICLES_IN_GAME;
v = AllocateVehicle(); if (!(flags & DC_QUERY_COST)) {
if (v == NULL) Vehicle *vl[num_vehicles];
return CMD_ERROR;
if (!AllocateVehicles(vl, num_vehicles))
return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
byte img = rvi->image_index; byte img = rvi->image_index;
Vehicle *u, *w; Vehicle *u, *w;
uint dir; uint dir;
v = vl[0];
v->spritenum = img; v->spritenum = img;
u = NULL; u = NULL;
@ -492,6 +574,8 @@ static int32 CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags)
v->type = VEH_Train; v->type = VEH_Train;
v->cur_image = 0xAC2; v->cur_image = 0xAC2;
AddArticulatedParts(rvi, vl);
_new_wagon_id = v->index; _new_wagon_id = v->index;
_new_vehicle_id = v->index; _new_vehicle_id = v->index;
@ -581,10 +665,11 @@ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{ {
const RailVehicleInfo *rvi; const RailVehicleInfo *rvi;
int value; int value;
Vehicle *v, *u; Vehicle *v;
UnitID unit_num; UnitID unit_num;
Engine *e; Engine *e;
TileIndex tile = TileVirtXY(x, y); TileIndex tile = TileVirtXY(x, y);
uint num_vehicles;
/* Check if the engine-type is valid (for the player) */ /* Check if the engine-type is valid (for the player) */
if (!IsEngineBuildable(p1, VEH_Train)) return CMD_ERROR; if (!IsEngineBuildable(p1, VEH_Train)) return CMD_ERROR;
@ -612,12 +697,16 @@ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
if (rvi->flags&RVI_MULTIHEAD && HASBIT(p2,0)) if (rvi->flags&RVI_MULTIHEAD && HASBIT(p2,0))
value /= 2; value /= 2;
num_vehicles = (rvi->flags & RVI_MULTIHEAD && HASBIT(p2, 0)) ? 2 : 1;
num_vehicles += CountArticulatedParts(rvi, p1);
if (!(flags & DC_QUERY_COST)) { if (!(flags & DC_QUERY_COST)) {
v = AllocateVehicle(); Vehicle *vl[num_vehicles];
if (v == NULL || IsOrderPoolFull()) if (!AllocateVehicles(vl, num_vehicles) || IsOrderPoolFull())
return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
v = vl[0];
unit_num = GetFreeUnitNumber(VEH_Train); unit_num = GetFreeUnitNumber(VEH_Train);
if (unit_num > _patches.max_trains) if (unit_num > _patches.max_trains)
return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
@ -665,8 +754,10 @@ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
VehiclePositionChanged(v); VehiclePositionChanged(v);
if (rvi->flags&RVI_MULTIHEAD && (u = AllocateVehicle()) != NULL && !HASBIT(p2,0)) { if (rvi->flags & RVI_MULTIHEAD && !HASBIT(p2, 0)) {
AddRearEngineToMultiheadedTrain(v, u, true); AddRearEngineToMultiheadedTrain(vl[0], vl[1], true);
} else {
AddArticulatedParts(rvi, vl);
} }
TrainConsistChanged(v); TrainConsistChanged(v);
@ -716,23 +807,27 @@ int CheckTrainStoppedInDepot(const Vehicle *v)
return count; return count;
} }
// unlink a rail wagon from the linked list. /**
// returns the new value of first * Unlink a rail wagon from the consist.
* @param v Vehicle to remove.
* @param first The first vehicle of the consist.
* @return The first vehicle of the consist.
*/
static Vehicle *UnlinkWagon(Vehicle *v, Vehicle *first) static Vehicle *UnlinkWagon(Vehicle *v, Vehicle *first)
{ {
Vehicle *u; Vehicle *u;
// unlinking the first vehicle of the chain? // unlinking the first vehicle of the chain?
if (v == first) { if (v == first) {
v = v->next; v = GetNextVehicle(v);
if (v == NULL) return NULL; if (v == NULL) return NULL;
v->subtype = TS_Free_Car; v->subtype = TS_Free_Car;
return v; return v;
} }
for (u = first; u->next != v; u = u->next) {} for (u = first; GetNextVehicle(u) != v; u = GetNextVehicle(u)) {}
u->next = v->next; GetLastEnginePart(u)->next = GetNextVehicle(v);
return first; return first;
} }
@ -788,6 +883,12 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
dst = GetVehicle(d); dst = GetVehicle(d);
} }
// if an articulated part is being handled, deal with its parent vehicle
while (src->subtype == TS_Artic_Part) src = GetPrevVehicleInChain(src);
if (dst != NULL) {
while (dst->subtype == TS_Artic_Part) dst = GetPrevVehicleInChain(dst);
}
// don't move the same vehicle.. // don't move the same vehicle..
if (src == dst) return 0; if (src == dst) return 0;
@ -798,7 +899,11 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
/* locate the head of the two chains */ /* locate the head of the two chains */
src_head = GetFirstVehicleInChain(src); src_head = GetFirstVehicleInChain(src);
dst_head = NULL; dst_head = NULL;
if (dst != NULL) dst_head = GetFirstVehicleInChain(dst); if (dst != NULL) {
dst_head = GetFirstVehicleInChain(dst);
// Now deal with articulated part of destination wagon
dst = GetLastEnginePart(dst);
}
/* clear the ->first cache */ /* clear the ->first cache */
{ {
@ -852,8 +957,8 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
// unlink ALL wagons // unlink ALL wagons
if (src != src_head) { if (src != src_head) {
Vehicle *v = src_head; Vehicle *v = src_head;
while (v->next != src) v=v->next; while (GetNextVehicle(v) != src) v = GetNextVehicle(v);
v->next = NULL; GetLastEnginePart(v)->next = NULL;
} else { } else {
src_head = NULL; src_head = NULL;
} }
@ -863,7 +968,7 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
dst_head = NULL; dst_head = NULL;
// unlink single wagon from linked list // unlink single wagon from linked list
src_head = UnlinkWagon(src, src_head); src_head = UnlinkWagon(src, src_head);
src->next = NULL; GetLastEnginePart(src)->next = NULL;
} }
if (dst == NULL) { if (dst == NULL) {
@ -893,8 +998,8 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{ {
Vehicle *v; Vehicle *v;
for (v = src; v->next != NULL; v = v->next) {}; for (v = src; GetNextVehicle(v) != NULL; v = GetNextVehicle(v));
v->next = dst->next; GetLastEnginePart(v)->next = dst->next;
} }
dst->next = src; dst->next = src;
} }
@ -999,6 +1104,7 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2)
SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
while (v->subtype == TS_Artic_Part) v = GetPrevVehicleInChain(v);
first = GetFirstVehicleInChain(v); first = GetFirstVehicleInChain(v);
// make sure the vehicle is stopped in the depot // make sure the vehicle is stopped in the depot
@ -1032,7 +1138,7 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2)
/* 2. We are selling the first engine, some special action might be required /* 2. We are selling the first engine, some special action might be required
* here, so take attention */ * here, so take attention */
if ((flags & DC_EXEC) && v == first) { if ((flags & DC_EXEC) && v == first) {
Vehicle *new_f = first->next; Vehicle *new_f = GetNextVehicle(first);
/* 2.1 If the first wagon is sold, update the first-> pointers to NULL */ /* 2.1 If the first wagon is sold, update the first-> pointers to NULL */
for (tmp = first; tmp != NULL; tmp = tmp->next) tmp->first = NULL; for (tmp = first; tmp != NULL; tmp = tmp->next) tmp->first = NULL;
@ -1082,7 +1188,7 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2)
* engines to its train anyways */ * engines to its train anyways */
if (p2 == 2 && ori_subtype == TS_Front_Engine) { if (p2 == 2 && ori_subtype == TS_Front_Engine) {
for (v = first; v != NULL; v = tmp) { for (v = first; v != NULL; v = tmp) {
tmp = v->next; tmp = GetNextVehicle(v);
DoCommandByTile(v->tile, v->index | INVALID_VEHICLE << 16, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); DoCommandByTile(v->tile, v->index | INVALID_VEHICLE << 16, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
} }
} }
@ -1093,7 +1199,7 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2)
* to be able to deduce which ones go with which ones */ * to be able to deduce which ones go with which ones */
int enf_count = 0; int enf_count = 0;
int enr_count = 0; int enr_count = 0;
for (tmp = first; tmp != NULL; tmp = tmp->next) { for (tmp = first; tmp != NULL; tmp = GetNextVehicle(tmp)) {
if (RailVehInfo(tmp->engine_type)->flags & RVI_MULTIHEAD) if (RailVehInfo(tmp->engine_type)->flags & RVI_MULTIHEAD)
(IS_FIRSTHEAD_SPRITE(tmp->spritenum)) ? enf_count++ : enr_count++; (IS_FIRSTHEAD_SPRITE(tmp->spritenum)) ? enf_count++ : enr_count++;
} }
@ -1102,7 +1208,7 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2)
* If we encounter a matching rear-engine to a front-engine * If we encounter a matching rear-engine to a front-engine
* earlier in the chain (before deletion), leave it alone */ * earlier in the chain (before deletion), leave it alone */
for (; v != NULL; v = tmp) { for (; v != NULL; v = tmp) {
tmp = v->next; tmp = GetNextVehicle(v);
if (RailVehInfo(v->engine_type)->flags & RVI_MULTIHEAD) { if (RailVehInfo(v->engine_type)->flags & RVI_MULTIHEAD) {
if (IS_FIRSTHEAD_SPRITE(v->spritenum)) { if (IS_FIRSTHEAD_SPRITE(v->spritenum)) {

View File

@ -530,6 +530,9 @@ found_it:
if (v == NULL) break; if (v == NULL) break;
} }
// if an articulated part was selected, find its parent
while (v != NULL && v->subtype == TS_Artic_Part) v = GetPrevVehicleInChain(v);
d->wagon = v; d->wagon = v;
return 0; return 0;
@ -1136,7 +1139,7 @@ static void DrawTrainDetailsWindow(Window *w)
tot_cargo[u->cargo_type][0] += u->cargo_count; tot_cargo[u->cargo_type][0] += u->cargo_count;
tot_cargo[u->cargo_type][1] += u->cargo_cap; tot_cargo[u->cargo_type][1] += u->cargo_cap;
} }
} while ( (u = u->next) != NULL); } while ((u = GetNextVehicle(u)) != NULL);
/* set scroll-amount seperately from counting, as to not /* set scroll-amount seperately from counting, as to not
compute num double for more carriages of the same type compute num double for more carriages of the same type
@ -1197,11 +1200,17 @@ static void DrawTrainDetailsWindow(Window *w)
if (det_tab != 3) { if (det_tab != 3) {
for(;;) { for(;;) {
if (--sel < 0 && sel >= -6) { if (--sel < 0 && sel >= -6) {
DrawTrainImage(v, x, y, 1, 0, INVALID_VEHICLE); int dx = 0;
_train_details_drawer_proc[WP(w,traindetails_d).tab](v, x + 30, y + 2); u = v;
do {
DrawTrainImage(u, x + WagonLengthToPixels(dx), y, 1, 0, INVALID_VEHICLE);
dx += u->u.rail.cached_veh_length;
u = u->next;
} while (u != NULL && u->subtype == TS_Artic_Part);
_train_details_drawer_proc[WP(w,traindetails_d).tab](v, x + WagonLengthToPixels(dx) + 2, y + 2);
y += 14; y += 14;
} }
if ( (v=v->next) == NULL) if ((v = GetNextVehicle(v)) == NULL)
return; return;
} }
} }

View File

@ -519,20 +519,28 @@ int CountVehiclesInChain(Vehicle *v)
void DeleteVehicle(Vehicle *v) void DeleteVehicle(Vehicle *v)
{ {
DeleteName(v->string_id); Vehicle *u;
v->type = 0; bool has_artic_part = false;
UpdateVehiclePosHash(v, INVALID_COORD, 0);
v->next_hash = INVALID_VEHICLE;
if (v->orders != NULL) do {
DeleteVehicleOrders(v); u = v->next;
has_artic_part = EngineHasArticPart(v);
DeleteName(v->string_id);
v->type = 0;
UpdateVehiclePosHash(v, INVALID_COORD, 0);
v->next_hash = INVALID_VEHICLE;
if (v->orders != NULL)
DeleteVehicleOrders(v);
v = u;
} while (v != NULL && has_artic_part);
} }
void DeleteVehicleChain(Vehicle *v) void DeleteVehicleChain(Vehicle *v)
{ {
do { do {
Vehicle *u = v; Vehicle *u = v;
v = v->next; v = GetNextVehicle(v);
DeleteVehicle(u); DeleteVehicle(u);
} while (v != NULL); } while (v != NULL);
} }
@ -1486,6 +1494,8 @@ static Vehicle *GetNextEnginePart(Vehicle *v)
const RailVehicleInfo *rvi = RailVehInfo(v->engine_type); const RailVehicleInfo *rvi = RailVehInfo(v->engine_type);
if (rvi->flags & RVI_MULTIHEAD) if (rvi->flags & RVI_MULTIHEAD)
return GetRearEngine(v, v->engine_type); return GetRearEngine(v, v->engine_type);
if (v->next != NULL && v->next->subtype == TS_Artic_Part)
return v->next;
} }
break; break;
case VEH_Aircraft: case VEH_Aircraft:
@ -1579,7 +1589,7 @@ int32 CmdCloneVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
} }
w_rear = w; // trains needs to know the last car in the train, so they can add more in next loop w_rear = w; // trains needs to know the last car in the train, so they can add more in next loop
} }
} while (v->type == VEH_Train && (v=v->next) != NULL); } while (v->type == VEH_Train && (v = GetNextVehicle(v)) != NULL);
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
v = v_front; v = v_front;
@ -1755,7 +1765,7 @@ static void MaybeReplaceVehicle(Vehicle *v)
break; break;
cost += temp_cost; cost += temp_cost;
} while (w->type == VEH_Train && (w=w->next) != NULL); } while (w->type == VEH_Train && (w = GetNextVehicle(w)) != NULL);
if (!(flags & DC_EXEC) && (CmdFailed(temp_cost) || p->money64 < (int32)(cost + p->engine_renew_money) || cost == 0)) { if (!(flags & DC_EXEC) && (CmdFailed(temp_cost) || p->money64 < (int32)(cost + p->engine_renew_money) || cost == 0)) {
if (p->money64 < (int32)(cost + p->engine_renew_money) && ( _local_player == v->owner ) && cost != 0) { if (p->money64 < (int32)(cost + p->engine_renew_money) && ( _local_player == v->owner ) && cost != 0) {

View File

@ -30,6 +30,7 @@ enum VehStatus {
// 1 and 3 do not appear to be used // 1 and 3 do not appear to be used
typedef enum TrainSubtypes { typedef enum TrainSubtypes {
TS_Front_Engine = 0, // Leading engine of a train TS_Front_Engine = 0, // Leading engine of a train
TS_Artic_Part = 1, // Articulated part of an engine
TS_Not_First = 2, // Wagon or additional engine TS_Not_First = 2, // Wagon or additional engine
TS_Free_Car = 4, // First in a wagon chain (in depot) TS_Free_Car = 4, // First in a wagon chain (in depot)
} TrainSubtype; } TrainSubtype;
@ -414,6 +415,41 @@ static inline bool IsVehicleIndex(uint index)
return index < GetVehiclePoolSize(); return index < GetVehiclePoolSize();
} }
/**
* Get the next real (non-articulated part) vehicle in the consist.
* @param v Vehicle.
* @return Next vehicle in the consist.
*/
static inline Vehicle *GetNextVehicle(const Vehicle *v)
{
Vehicle *u = v->next;
while (u != NULL && u->subtype == TS_Artic_Part) {
u = u->next;
}
return u;
}
/**
* Check if an engine has an articulated part.
* @param v Vehicle.
* @return True if the engine has an articulated part.
*/
static inline bool EngineHasArticPart(const Vehicle *v)
{
return (v->next != NULL && v->next->subtype == TS_Artic_Part);
}
/**
* Get the last part of a multi-part engine.
* @param v Vehicle.
* @return Last part of the engine.
*/
static inline Vehicle *GetLastEnginePart(Vehicle *v)
{
while (EngineHasArticPart(v)) v = v->next;
return v;
}
/* Returns order 'index' of a vehicle or NULL when it doesn't exists */ /* Returns order 'index' of a vehicle or NULL when it doesn't exists */
static inline Order *GetVehicleOrder(const Vehicle *v, int index) static inline Order *GetVehicleOrder(const Vehicle *v, int index)
{ {