diff options
author | bjarni <bjarni@openttd.org> | 2005-11-18 23:41:03 +0000 |
---|---|---|
committer | bjarni <bjarni@openttd.org> | 2005-11-18 23:41:03 +0000 |
commit | 22a46798378768271af6b90cd41327155a909dd2 (patch) | |
tree | 3dd0d12983f0eaa08af460475f1f8b0a2c961d75 /train_cmd.c | |
parent | 40ec9bb1235a2231f0c883baaae73e106c67f1cf (diff) | |
download | openttd-22a46798378768271af6b90cd41327155a909dd2.tar.xz |
(svn r3218) -Feature: Multiheaded train engines will now stay in the same train
This means that any user attempt to remove a rear engine will tell the user to move the front engine instead
This fixes the assert when moving multiheaded engines (introduced in r3144)
Note: to make old savegames use this feature, some engines might be turned around in order to link engines in pairs
-Codechange: train subtype is now a bitmask
This allows fast access to info like if it is a wagon or engine and if it is in front and so on
Note: savegame version bump
Diffstat (limited to 'train_cmd.c')
-rw-r--r-- | train_cmd.c | 347 |
1 files changed, 216 insertions, 131 deletions
diff --git a/train_cmd.c b/train_cmd.c index f933984e7..116600dc4 100644 --- a/train_cmd.c +++ b/train_cmd.c @@ -22,9 +22,7 @@ #include "debug.h" #include "waypoint.h" #include "vehicle_gui.h" - -#define IS_FIRSTHEAD_SPRITE(spritenum) \ - (is_custom_sprite(spritenum) ? IS_CUSTOM_FIRSTHEAD_SPRITE(spritenum) : _engine_sprite_add[spritenum] == 0) +#include "train.h" static bool TrainCheckIfLineEnds(Vehicle *v); static void TrainController(Vehicle *v); @@ -50,7 +48,7 @@ void TrainCargoChanged(Vehicle* v) vweight += (_cargoc.weights[u->cargo_type] * u->cargo_count) / 16; // Vehicle weight is not added for articulated parts. - if (u->subtype != TS_Artic_Part) { + if (!IsArticulatedPart(u)) { // vehicle weight is the sum of the weight of the vehicle and the weight of its cargo vweight += rvi->weight; @@ -86,10 +84,10 @@ void TrainConsistChanged(Vehicle* v) assert(v->type == VEH_Train); - assert(v->subtype == TS_Front_Engine || v->subtype == TS_Free_Car); + assert(IsFrontEngine(v) || IsFreeWagon(v)); rvi_v = RailVehInfo(v->engine_type); - first_engine = (v->subtype == TS_Front_Engine) ? v->engine_type : INVALID_VEHICLE; + first_engine = IsFrontEngine(v) ? v->engine_type : INVALID_VEHICLE; v->u.rail.cached_total_length = 0; for (u = v; u != NULL; u = u->next) { @@ -102,7 +100,7 @@ void TrainConsistChanged(Vehicle* v) if (rvi_u->visual_effect != 0) { u->u.rail.cached_vis_effect = rvi_u->visual_effect; } else { - if (rvi_u->flags & RVI_WAGON || u->subtype == TS_Artic_Part) { + if (IsTrainWagon(u) || IsArticulatedPart(u)) { // Wagons and articulated parts have no effect by default u->u.rail.cached_vis_effect = 0x40; } else if (rvi_u->engclass == 0) { @@ -114,7 +112,7 @@ void TrainConsistChanged(Vehicle* v) } } - if (u->subtype != TS_Artic_Part) { + if (!IsArticulatedPart(u)) { // power is the sum of the powers of all engines and powered wagons in the consist power += rvi_u->power; @@ -277,7 +275,7 @@ static int GetTrainAcceleration(Vehicle *v, bool mode) max_speed += (max_speed / 2) * v->u.rail.railtype; - if (IsTileType(v->tile, MP_STATION) && v->subtype == TS_Front_Engine) { + if (IsTileType(v->tile, MP_STATION) && IsFrontEngine(v)) { if (TrainShouldStop(v, v->tile)) { int station_length = 0; TileIndex tile = v->tile; @@ -361,7 +359,7 @@ void UpdateTrainAcceleration(Vehicle *v) uint power = 0; uint weight = 0; - assert(v->subtype == TS_Front_Engine); + assert(IsFrontEngine(v)); weight = v->u.rail.cached_weight; power = v->u.rail.cached_power; @@ -491,7 +489,8 @@ static void AddArticulatedParts(const RailVehicleInfo *rvi, Vehicle **vl) u->engine_type = engine_type; u->value = 0; u->type = VEH_Train; - u->subtype = TS_Artic_Part; + u->subtype = 0; + SetArticulatedPart(u); u->cur_image = 0xAC2; VehiclePositionChanged(u); @@ -531,7 +530,7 @@ static int32 CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags) FOR_ALL_VEHICLES(w) { if (w->type == VEH_Train && w->tile == tile && - w->subtype == TS_Free_Car && w->engine_type == engine) { + IsFreeWagon(w) && w->engine_type == engine) { u = GetLastVehicleInChain(w); break; } @@ -555,10 +554,12 @@ static int32 CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags) v->u.rail.track = 0x80; v->vehstatus = VS_HIDDEN | VS_DEFPAL; - v->subtype = TS_Free_Car; + v->subtype = 0; + SetTrainWagon(v); if (u != NULL) { u->next = v; - v->subtype = TS_Not_First; + } else { + SetFreeWagon(v); } v->cargo_type = rvi->cargo_type; @@ -593,11 +594,11 @@ static void NormalizeTrainVehInDepot(const Vehicle* u) const Vehicle* v; FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Train && v->subtype == TS_Free_Car && + if (v->type == VEH_Train && IsFreeWagon(v) && v->tile == u->tile && v->u.rail.track == 0x80) { - if (DoCommandByTile(0, v->index | (u->index << 16), 1, DC_EXEC, - CMD_MOVE_RAIL_VEHICLE) == CMD_ERROR) + if (CmdFailed(DoCommandByTile(0, v->index | (u->index << 16), 1, DC_EXEC, + CMD_MOVE_RAIL_VEHICLE))) break; } } @@ -638,7 +639,8 @@ void AddRearEngineToMultiheadedTrain(Vehicle *v, Vehicle *u, bool building) u->z_height = 6; u->u.rail.track = 0x80; u->vehstatus = v->vehstatus & ~VS_STOPPED; - u->subtype = TS_Not_First; + u->subtype = 0; + SetMultiheaded(u); u->spritenum = v->spritenum + 1; u->cargo_type = v->cargo_type; u->cargo_cap = v->cargo_cap; @@ -750,13 +752,24 @@ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) v->type = VEH_Train; v->cur_image = 0xAC2; + v->subtype = 0; + SetFrontEngine(v); + SetTrainEngine(v); + v->u.rail.shortest_platform[0] = 255; v->u.rail.shortest_platform[1] = 0; VehiclePositionChanged(v); if (rvi->flags & RVI_MULTIHEAD && !HASBIT(p2, 0)) { + SetMultiheaded(v); AddRearEngineToMultiheadedTrain(vl[0], vl[1], true); + /* Now we need to link the front and rear engines together + * other_multiheaded_part is the pointer that links to the other half of the engine + * vl[0] is the front and vl[1] is the rear + */ + vl[0]->u.rail.other_multiheaded_part = vl[1]; + vl[1]->u.rail.other_multiheaded_part = vl[0]; } else { AddArticulatedParts(rvi, vl); } @@ -800,7 +813,7 @@ int CheckTrainStoppedInDepot(const Vehicle *v) for (; v != NULL; v = v->next) { count++; if (v->u.rail.track != 0x80 || v->tile != tile || - (v->subtype == TS_Front_Engine && !(v->vehstatus & VS_STOPPED))) { + (IsFrontEngine(v) && !(v->vehstatus & VS_STOPPED))) { _error_message = STR_881A_TRAINS_CAN_ONLY_BE_ALTERED; return -1; } @@ -824,7 +837,8 @@ static Vehicle *UnlinkWagon(Vehicle *v, Vehicle *first) v = GetNextVehicle(v); if (v == NULL) return NULL; - v->subtype = TS_Free_Car; + if (IsTrainWagon(v)) SetFreeWagon(v); + return v; } @@ -840,7 +854,7 @@ static Vehicle *FindGoodVehiclePos(const Vehicle *src) TileIndex tile = src->tile; FOR_ALL_VEHICLES(dst) { - if (dst->type == VEH_Train && dst->subtype == TS_Free_Car && + if (dst->type == VEH_Train && IsFreeWagon(dst) && dst->tile == tile) { // check so all vehicles in the line have the same engine. Vehicle *v = dst; @@ -855,6 +869,45 @@ static Vehicle *FindGoodVehiclePos(const Vehicle *src) return NULL; } +/* + * add a vehicle v behind vehicle dest + * use this function since it sets flags as needed + */ +static void AddWagonToConsist(Vehicle *v, Vehicle *dest) +{ + UnlinkWagon(v, GetFirstVehicleInChain(v)); + if (dest == NULL) return; + + v->next = dest->next; + dest->next = v; + ClearFreeWagon(v); + ClearFrontEngine(v); +} + +/* + * move around on the train so rear engines are placed correctly according to the other engines + * always call with the front engine + */ +static void NormaliseTrainConsist(Vehicle *v) +{ + Vehicle *u; + + if (IsFreeWagon(v)) return; + + assert(IsFrontEngine(v)); + + for(; v != NULL; v = GetNextVehicle(v)) { + if (!IsMultiheaded(v) || !IsTrainEngine(v)) continue; + + /* make sure that there are no free cars before next engine */ + for(u = v; u->next != NULL && !IsTrainEngine(u->next); u = u->next); + + if (u == v->u.rail.other_multiheaded_part) continue; + AddWagonToConsist(v->u.rail.other_multiheaded_part, u); + + } +} + /** Move a rail vehicle around inside the depot. * @param x,y unused * @param p1 various bitstuffed elements @@ -867,7 +920,6 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) VehicleID s = GB(p1, 0, 16); VehicleID d = GB(p1, 16, 16); Vehicle *src, *dst, *src_head, *dst_head; - bool is_loco; if (!IsVehicleIndex(s)) return CMD_ERROR; @@ -875,20 +927,18 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (src->type != VEH_Train) return CMD_ERROR; - is_loco = !(RailVehInfo(src->engine_type)->flags & RVI_WAGON) && IS_FIRSTHEAD_SPRITE(src->spritenum); - // if nothing is selected as destination, try and find a matching vehicle to drag to. if (d == INVALID_VEHICLE) { dst = NULL; - if (!is_loco) dst = FindGoodVehiclePos(src); + if (!IsTrainEngine(src)) dst = FindGoodVehiclePos(src); } else { dst = GetVehicle(d); } // if an articulated part is being handled, deal with its parent vehicle - while (src->subtype == TS_Artic_Part) src = GetPrevVehicleInChain(src); + while (IsArticulatedPart(src)) src = GetPrevVehicleInChain(src); if (dst != NULL) { - while (dst->subtype == TS_Artic_Part) dst = GetPrevVehicleInChain(dst); + while (IsArticulatedPart(dst)) dst = GetPrevVehicleInChain(dst); } // don't move the same vehicle.. @@ -907,14 +957,42 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) dst = GetLastEnginePart(dst); } - /* clear the ->first cache */ - { - Vehicle *u; + if (dst != NULL && IsMultiheaded(dst) && !IsTrainEngine(dst) && IsTrainWagon(src)) { + /* We are moving a wagon to the rear part of a multiheaded engine */ + if (dst->next == NULL) { + /* It's the last one, so we will add the wagon just before the rear engine */ + dst = GetPrevVehicleInChain(dst); + // if dst is NULL, it means that dst got a rear multiheaded engine as first engine. We can't use that + if (dst == NULL) return CMD_ERROR; + } else { + /* there are more units on this train, so we will add the wagon after the next one*/ + dst = dst->next; + } + } + + if (IsTrainEngine(src) && dst_head != NULL) { + /* we need to make sure that we didn't place it between a pair of multiheaded engines */ + Vehicle *u, *engine = NULL; + + for(u = dst_head; u != NULL; u = u->next) { + if (IsTrainEngine(u) && IsMultiheaded(u) && u->u.rail.other_multiheaded_part != NULL) { + engine = u; + } + if (engine != NULL && engine->u.rail.other_multiheaded_part == u) { + engine = NULL; + } + if (u == dst) { + if (engine != NULL) { + dst = engine->u.rail.other_multiheaded_part; + } + break; + } - for (u = src_head; u != NULL; u = u->next) u->first = NULL; - for (u = dst_head; u != NULL; u = u->next) u->first = NULL; + } } + if (IsMultiheaded(src) && !IsTrainEngine(src)) return_cmd_error(STR_REAR_ENGINE_FOLLOW_FRONT_ERROR); + /* check if all vehicles in the source train are stopped inside a depot */ if (CheckTrainStoppedInDepot(src_head) < 0) return CMD_ERROR; @@ -924,18 +1002,9 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) int num = CheckTrainStoppedInDepot(dst_head); if (num < 0) return CMD_ERROR; - if (num > (_patches.mammoth_trains ? 100 : 9) && dst_head->subtype == TS_Front_Engine ) + if (num > (_patches.mammoth_trains ? 100 : 9) && IsFrontEngine(dst_head)) return_cmd_error(STR_8819_TRAIN_TOO_LONG); - // if it's a multiheaded vehicle we're dragging to, drag to the vehicle before.. - while (IS_CUSTOM_SECONDHEAD_SPRITE(dst->spritenum) || ( - !is_custom_sprite(dst->spritenum) && _engine_sprite_add[dst->spritenum] != 0) - ) { - Vehicle *v = GetPrevVehicleInChain(dst); - if (v == NULL || src == v) break; - dst = v; - } - assert(dst_head->tile == src_head->tile); } @@ -943,7 +1012,7 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (HASBIT(p2, 0) && src_head == dst_head) return 0; // moving a loco to a new line?, then we need to assign a unitnumber. - if (dst == NULL && src->subtype != TS_Front_Engine && is_loco) { + if (dst == NULL && !IsFrontEngine(src) && IsTrainEngine(src)) { UnitID unit_num = GetFreeUnitNumber(VEH_Train); if (unit_num > _patches.max_trains) return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); @@ -955,8 +1024,13 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) /* do it? */ if (flags & DC_EXEC) { - Vehicle *new_front = GetNextVehicle(src); //used if next in line should make a train on it's own - bool make_new_front = src->subtype == TS_Front_Engine; + /* clear the ->first cache */ + { + Vehicle *u; + + for (u = src_head; u != NULL; u = u->next) u->first = NULL; + for (u = dst_head; u != NULL; u = u->next) u->first = NULL; + } if (HASBIT(p2, 0)) { // unlink ALL wagons @@ -977,26 +1051,27 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) } if (dst == NULL) { - // move the train to an empty line. for locomotives, we set the type to 0. for wagons, 4. - if (is_loco) { - if (src->subtype != TS_Front_Engine) { + // move the train to an empty line. for locomotives, we set the type to TS_Front. for wagons, 4. + if (IsTrainEngine(src)) { + if (!IsFrontEngine(src)) { // setting the type to 0 also involves setting up the orders field. - src->subtype = TS_Front_Engine; + SetFrontEngine(src); assert(src->orders == NULL); src->num_orders = 0; } } else { - src->subtype = TS_Free_Car; + SetFreeWagon(src); } dst_head = src; } else { - if (src->subtype == TS_Front_Engine) { + if (IsFrontEngine(src)) { // the vehicle was previously a loco. need to free the order list and delete vehicle windows etc. DeleteWindowById(WC_VEHICLE_VIEW, src->index); DeleteVehicleOrders(src); } - src->subtype = TS_Not_First; + ClearFrontEngine(src); + ClearFreeWagon(src); src->unitnumber = 0; // doesn't occupy a unitnumber anymore. // link in the wagon(s) in the chain. @@ -1008,19 +1083,45 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) } dst->next = src; } + if (src->u.rail.other_multiheaded_part != NULL) { + if (src->u.rail.other_multiheaded_part == src_head) { + src_head = src_head->next; + } + AddWagonToConsist(src->u.rail.other_multiheaded_part, src); + } + + if (HASBIT(p2, 0) && src_head != NULL && src_head != src) { + /* if we stole a rear multiheaded engine, we better give it back to the front end */ + Vehicle *engine = NULL, *u; + for (u = src_head; u != NULL; u = u->next) { + if (IsMultiheaded(u)) { + if (IsTrainEngine(u)) { + engine = u; + continue; + } + /* we got the rear engine to match with the front one */ + engine = NULL; + } + } + if (engine != NULL && engine->u.rail.other_multiheaded_part != NULL) { + AddWagonToConsist(engine->u.rail.other_multiheaded_part, engine); + // previous line set the front engine to the old front. We need to clear that + engine->u.rail.other_multiheaded_part->first = NULL; + } + } /* If there is an engine behind first_engine we moved away, it should become new first_engine * To do this, CmdMoveRailVehicle must be called once more - * since we set p2 to a condition that makes the statement false, we can't loop forever with this one */ - if (make_new_front && new_front != NULL && !(HASBIT(p2, 0))) { - if (!(RailVehInfo(new_front->engine_type)->flags & RVI_WAGON)) { - CmdMoveRailVehicle(x, y, flags, new_front->index | (INVALID_VEHICLE << 16), 1); - } + * we can't loop forever here because next time we reach this line we will have a front engine */ + if (src_head != NULL && !IsFrontEngine(src_head) && IsTrainEngine(src_head)) { + CmdMoveRailVehicle(x, y, flags, src_head->index | (INVALID_VEHICLE << 16), 1); + src_head = NULL; // don't do anything more to this train since the new call will do it } if (src_head) { + NormaliseTrainConsist(src_head); TrainConsistChanged(src_head); - if (src_head->subtype == TS_Front_Engine) { + if (IsFrontEngine(src_head)) { UpdateTrainAcceleration(src_head); InvalidateWindow(WC_VEHICLE_DETAILS, src_head->index); /* Update the refit button and window */ @@ -1032,8 +1133,9 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) }; if (dst_head) { + NormaliseTrainConsist(dst_head); TrainConsistChanged(dst_head); - if (dst_head->subtype == TS_Front_Engine) { + if (IsFrontEngine(dst_head)) { UpdateTrainAcceleration(dst_head); InvalidateWindow(WC_VEHICLE_DETAILS, dst_head->index); /* Update the refit button and window */ @@ -1074,27 +1176,6 @@ int32 CmdStartStopTrain(int x, int y, uint32 flags, uint32 p1, uint32 p2) return 0; } -/** - * Search for a matching rear-engine of a dual-headed train. - * Do this as if you would find matching parentheses. If a new - * engine is 'started', first 'close' that before 'closing' our - * searched engine - */ -Vehicle* GetRearEngine(const Vehicle* v) -{ - Vehicle *u; - int en_count = 1; - - for (u = v->next; u != NULL; u = u->next) { - if (u->engine_type == v->engine_type) { // find matching engine - en_count += (IS_FIRSTHEAD_SPRITE(u->spritenum)) ? +1 : -1; - - if (en_count == 0) return (Vehicle *)u; - } - } - return NULL; -} - /** Sell a (single) train wagon/engine. * @param x,y unused * @param p1 the wagon/engine index @@ -1108,6 +1189,7 @@ Vehicle* GetRearEngine(const Vehicle* v) int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2) { Vehicle *v, *tmp, *first; + Vehicle *new_f = NULL; int32 cost = 0; if (!IsVehicleIndex(p1) || p2 > 2) return CMD_ERROR; @@ -1118,14 +1200,16 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2) SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); - while (v->subtype == TS_Artic_Part) v = GetPrevVehicleInChain(v); + while (IsArticulatedPart(v)) v = GetPrevVehicleInChain(v); first = GetFirstVehicleInChain(v); // make sure the vehicle is stopped in the depot if (CheckTrainStoppedInDepot(first) < 0) return CMD_ERROR; + if (IsMultiheaded(v) && !IsTrainEngine(v)) return_cmd_error(STR_REAR_ENGINE_FOLLOW_FRONT_ERROR); + if (flags & DC_EXEC) { - if (v == first && first->subtype == TS_Front_Engine) { + if (v == first && IsFrontEngine(first)) { DeleteWindowById(WC_VEHICLE_VIEW, first->index); } if (IsLocalPlayer() && (p1 == 1 || !(RailVehInfo(v->engine_type)->flags & RVI_WAGON))) { @@ -1141,20 +1225,22 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2) byte ori_subtype = v->subtype; // backup subtype of deleted wagon in case DeleteVehicle() changes /* 1. Delete the engine, if it is dualheaded also delete the matching - * rear engine of the loco (from the point of deletion onwards) */ - Vehicle* rear = (RailVehInfo(v->engine_type)->flags & RVI_MULTIHEAD) ? GetRearEngine(v) : NULL; + * rear engine of the loco (from the point of deletion onwards) */ + Vehicle *rear = (IsMultiheaded(v) && + IsTrainEngine(v)) ? v->u.rail.other_multiheaded_part : NULL; + if (rear != NULL) { - cost -= v->value; + cost -= rear->value; if (flags & DC_EXEC) { - v = UnlinkWagon(rear, v); + UnlinkWagon(rear, first); DeleteVehicle(rear); } } /* 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) { - Vehicle *new_f = GetNextVehicle(first); + new_f = GetNextVehicle(first); /* 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; @@ -1163,7 +1249,7 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2) * if the second wagon (which will be first) is an engine. If it is one, * promote it as a new train, retaining the unitnumber, orders */ if (new_f != NULL) { - if (!(RailVehInfo(new_f->engine_type)->flags & RVI_WAGON) && IS_FIRSTHEAD_SPRITE(new_f->spritenum)) { + if (IsTrainEngine(new_f)) { switch_engine = true; /* Copy important data from the front engine */ new_f->unitnumber = first->unitnumber; @@ -1185,12 +1271,13 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2) /* 4 If the second wagon was an engine, update it to front_engine * which UnlinkWagon() has changed to TS_Free_Car */ - if (switch_engine) first->subtype = TS_Front_Engine; + if (switch_engine) SetFrontEngine(first); /* 5. If the train still exists, update its acceleration, window, etc. */ if (first != NULL) { + NormaliseTrainConsist(first); TrainConsistChanged(first); - if (first->subtype == TS_Front_Engine) { + if (IsFrontEngine(first)) { InvalidateWindow(WC_VEHICLE_DETAILS, first->index); InvalidateWindow(WC_VEHICLE_REFIT, first->index); UpdateTrainAcceleration(first); @@ -1199,10 +1286,10 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2) /* (6.) Borked AI. If it sells an engine it expects all wagons lined - * up on a new line to be added to the newly built loco. Replace it is. - * Totally braindead cause building a new engine adds all loco-less - * engines to its train anyways */ - if (p2 == 2 && ori_subtype == TS_Front_Engine) { + * up on a new line to be added to the newly built loco. Replace it is. + * Totally braindead cause building a new engine adds all loco-less + * engines to its train anyways */ + if (p2 == 2 && HASBIT(ori_subtype, Train_Front)) { for (v = first; v != NULL; v = tmp) { tmp = GetNextVehicle(v); DoCommandByTile(v->tile, v->index | INVALID_VEHICLE << 16, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); @@ -1211,31 +1298,26 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2) } } break; case 1: { /* Delete wagon and all wagons after it given certain criteria */ - /* 1. Count the number for first and rear engines for dualheads - * to be able to deduce which ones go with which ones */ - int enf_count = 0; - int enr_count = 0; - for (tmp = first; tmp != NULL; tmp = GetNextVehicle(tmp)) { - if (RailVehInfo(tmp->engine_type)->flags & RVI_MULTIHEAD) - (IS_FIRSTHEAD_SPRITE(tmp->spritenum)) ? enf_count++ : enr_count++; - } - - /* 2. Start deleting every vehicle after the selected one - * If we encounter a matching rear-engine to a front-engine - * earlier in the chain (before deletion), leave it alone */ + /* Start deleting every vehicle after the selected one + * If we encounter a matching rear-engine to a front-engine + * earlier in the chain (before deletion), leave it alone */ for (; v != NULL; v = tmp) { tmp = GetNextVehicle(v); - if (RailVehInfo(v->engine_type)->flags & RVI_MULTIHEAD) { - if (IS_FIRSTHEAD_SPRITE(v->spritenum)) { - /* Always delete newly encountered front-engines */ - enf_count--; - } else if (enr_count > enf_count) { - /* More rear engines than front engines means this rear-engine does - * not belong to any front-engine; delete */ - enr_count--; - } else { - /* Otherwise leave it alone */ + if (IsMultiheaded(v)) { + if (IsTrainEngine(v)) { + /* We got a front engine of a multiheaded set. Now we will sell the rear end too */ + Vehicle *rear = v->u.rail.other_multiheaded_part; + + if (rear != NULL) { + cost -= rear->value; + if (flags & DC_EXEC) { + first = UnlinkWagon(rear, first); + DeleteVehicle(rear); + } + } + } else if (v->u.rail.other_multiheaded_part != NULL) { + /* The front to this engine is earlier in this train. Do nothing */ continue; } } @@ -1249,9 +1331,12 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2) /* 3. If it is still a valid train after selling, update its acceleration and cached values */ if ((flags & DC_EXEC) && first != NULL) { + NormaliseTrainConsist(first); TrainConsistChanged(first); - if (first->subtype == TS_Front_Engine) + if (IsFrontEngine(first)) UpdateTrainAcceleration(first); + InvalidateWindow(WC_VEHICLE_DETAILS, first->index); + InvalidateWindow(WC_VEHICLE_REFIT, first->index); } } break; } @@ -2620,7 +2705,7 @@ static bool CheckCompatibleRail(const Vehicle *v, TileIndex tile) // tracks over roads, do owner check of tracks return IsTileOwner(tile, v->owner) && ( - v->subtype != TS_Front_Engine || + !IsFrontEngine(v) || IsCompatibleRail(v->u.rail.railtype, GB(_m[tile].m4, 0, 4)) ); @@ -2630,7 +2715,7 @@ static bool CheckCompatibleRail(const Vehicle *v, TileIndex tile) return IsTileOwner(tile, v->owner) && ( - v->subtype != TS_Front_Engine || + !IsFrontEngine(v) || IsCompatibleRail(v->u.rail.railtype, GetRailType(tile)) ); } @@ -2741,7 +2826,7 @@ static uint CountPassengersInTrain(const Vehicle* v) /* * Checks whether the specified train has a collision with another vehicle. If - * so, destroys this vehicle, and the other vehicle if its subtype is 0 (TS_Front_Engine). + * so, destroys this vehicle, and the other vehicle if its subtype has TS_Front. * Reports the incident in a flashy news item, modifies station ratings and * plays a sound. */ @@ -2778,7 +2863,7 @@ static void CheckTrainCollision(Vehicle *v) num += 2 + CountPassengersInTrain(coll); SetVehicleCrashed(v); - if (coll->subtype == TS_Front_Engine) SetVehicleCrashed(coll); + if (IsFrontEngine(coll)) SetVehicleCrashed(coll); SetDParam(0, num); AddNewsItem(STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL, @@ -2800,7 +2885,7 @@ static void *CheckVehicleAtSignal(Vehicle *v, void *data) { const VehicleAtSignalData* vasd = data; - if (v->type == VEH_Train && v->subtype == TS_Front_Engine && + if (v->type == VEH_Train && IsFrontEngine(v) && v->tile == vasd->tile) { byte diff = (v->direction - vasd->direction + 2) & 7; @@ -2970,7 +3055,7 @@ green_light: goto invalid_rail; } - if (v->subtype == TS_Front_Engine) v->load_unload_time_rem = 0; + if (IsFrontEngine(v)) v->load_unload_time_rem = 0; if (!(r&0x4)) { v->tile = gp.new_tile; @@ -2978,7 +3063,7 @@ green_light: assert(v->u.rail.track); } - if (v->subtype == TS_Front_Engine) + if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir); /* Signals can only change when the first @@ -3433,13 +3518,13 @@ void Train_Tick(Vehicle *v) v->tick_counter++; - if (v->subtype == TS_Front_Engine) { + if (IsFrontEngine(v)) { TrainLocoHandler(v, false); // make sure vehicle wasn't deleted. - if (v->type == VEH_Train && v->subtype == TS_Front_Engine) + if (v->type == VEH_Train && IsFrontEngine(v)) TrainLocoHandler(v, true); - } else if (v->subtype == TS_Free_Car && HASBITS(v->vehstatus, VS_CRASHED)) { + } else if (IsFreeWagon(v) && HASBITS(v->vehstatus, VS_CRASHED)) { // Delete flooded standalone wagon if (++v->u.rail.crash_anim_pos >= 4400) DeleteVehicle(v); @@ -3460,7 +3545,7 @@ void TrainEnterDepot(Vehicle *v, TileIndex tile) { SetSignalsOnBothDir(tile, _depot_track_ind[GB(_m[tile].m5, 0, 2)]); - if (v->subtype != TS_Front_Engine) v = GetFirstVehicleInChain(v); + if (!IsFrontEngine(v)) v = GetFirstVehicleInChain(v); VehicleServiceInDepot(v); @@ -3565,7 +3650,7 @@ void OnNewDay_Train(Vehicle *v) if ((++v->day_counter & 7) == 0) DecreaseVehicleValue(v); - if (v->subtype == TS_Front_Engine) { + if (IsFrontEngine(v)) { CheckVehicleBreakdown(v); AgeVehicle(v); @@ -3610,7 +3695,7 @@ void TrainsYearlyLoop(void) Vehicle *v; FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Train && v->subtype == TS_Front_Engine) { + if (v->type == VEH_Train && IsFrontEngine(v)) { // show warning if train is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) if (_patches.train_income_warn && v->owner == _local_player && v->age >= 730 && v->profit_this_year < 0) { |