From 02e770ff2cf77776252d1481456817950bf138a9 Mon Sep 17 00:00:00 2001 From: maedhros Date: Sun, 29 Apr 2007 08:43:00 +0000 Subject: [PATCH] (svn r9742) -Fix (r9689) [FS#739]: Fix cloning with refit costs again, hopefully for good this time. --- src/aircraft_cmd.cpp | 1 + src/command.cpp | 8 +++-- src/roadveh_cmd.cpp | 2 ++ src/ship_cmd.cpp | 2 ++ src/train_cmd.cpp | 5 ++- src/vehicle.cpp | 73 +++++++++++++++++++++++++++++--------------- 6 files changed, 63 insertions(+), 28 deletions(-) diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index 3950cae500..c68c063833 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -597,6 +597,7 @@ int32 CmdSendAircraftToHangar(TileIndex tile, uint32 flags, uint32 p1, uint32 p2 * @param p2 various bitstuffed elements * - p2 = (bit 0-7) - the new cargo type to refit to * - p2 = (bit 8-15) - the new cargo subtype to refit to + * - p2 = (bit 16) - refit only this vehicle (ignored) * @return cost of refit or error */ int32 CmdRefitAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) diff --git a/src/command.cpp b/src/command.cpp index 94717a38db..0b20d9820c 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -456,13 +456,17 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, * restrictions which may cause the test run to fail (the previous * road fragments still stay there and the town won't let you * disconnect the road system), but the exec will succeed and this - * fact will trigger an assertion failure. --pasky */ + * fact will trigger an assertion failure. --pasky + * CMD_CLONE_VEHICLE: Both building new vehicles and refitting them can be + * influenced by newgrf callbacks, which makes it impossible to accurately + * estimate the cost of cloning a vehicle. */ notest = (cmd & 0xFF) == CMD_CLEAR_AREA || (cmd & 0xFF) == CMD_CONVERT_RAIL || (cmd & 0xFF) == CMD_LEVEL_LAND || (cmd & 0xFF) == CMD_REMOVE_ROAD || - (cmd & 0xFF) == CMD_REMOVE_LONG_ROAD; + (cmd & 0xFF) == CMD_REMOVE_LONG_ROAD || + (cmd & 0xFF) == CMD_CLONE_VEHICLE; _docommand_recursive = 1; diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 3d7f220fa5..d0e3813dc7 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -1851,6 +1851,8 @@ void RoadVehiclesYearlyLoop() * @param p2 Bitstuffed elements * - p2 = (bit 0-7) - the new cargo type to refit to * - p2 = (bit 8-15) - the new cargo subtype to refit to + * - p2 = (bit 16) - refit only this vehicle (ignored) + * @return cost of refit or error */ int32 CmdRefitRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) { diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index dd86a960d3..58864273b3 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -1087,6 +1087,8 @@ int32 CmdSendShipToDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) * @param p2 various bitstuffed elements * - p2 = (bit 0-7) - the new cargo type to refit to (p2 & 0xFF) * - p2 = (bit 8-15) - the new cargo subtype to refit to + * - p2 = (bit 16) - refit only this vehicle (ignored) + * @return cost of refit or error */ int32 CmdRefitShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) { diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index bbdb775476..c3eedc2c52 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1751,11 +1751,14 @@ int32 CmdForceTrainProceed(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) * param p2 various bitstuffed elements * - p2 = (bit 0-7) - the new cargo type to refit to * - p2 = (bit 8-15) - the new cargo subtype to refit to + * - p2 = (bit 16) - refit only this vehicle + * @return cost of refit or error */ int32 CmdRefitRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) { CargoID new_cid = GB(p2, 0, 8); byte new_subtype = GB(p2, 8, 8); + bool only_this = HASBIT(p2, 16); if (!IsValidVehicleID(p1)) return CMD_ERROR; @@ -1833,7 +1836,7 @@ int32 CmdRefitRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) } } } - } while ((v = v->next) != NULL); + } while ((v = v->next) != NULL && !only_this); _returned_refit_capacity = num; diff --git a/src/vehicle.cpp b/src/vehicle.cpp index f030656b99..90ee45ad6f 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1804,7 +1804,7 @@ int32 CmdCloneVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) { Vehicle *v_front, *v; Vehicle *w_front, *w, *w_rear; - int cost, total_cost = 0; + int32 cost, total_cost = 0; uint32 build_argument = 2; if (!IsValidVehicleID(p1)) return CMD_ERROR; @@ -1842,18 +1842,6 @@ int32 CmdCloneVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) v = v_front; do { - - if (!(flags & DC_EXEC)) { - /* Get the refit cost. - * This is only needed when estimating as when the command is executed, the cost from the refit command is used. - * This needs to be done for every single unit, so it should be done before checking if it's a multiheaded engine. */ - CargoID new_cargo_type = GetEngineCargoType(v->engine_type); - - if (new_cargo_type != v->cargo_type && new_cargo_type != CT_INVALID) { - total_cost += GetRefitCost(v->engine_type); - } - } - if (IsMultiheaded(v) && !IsTrainEngine(v)) { /* we build the rear ends of multiheaded trains with the front ones */ continue; @@ -1869,18 +1857,6 @@ int32 CmdCloneVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) if (flags & DC_EXEC) { w = GetVehicle(_new_vehicle_id); - Vehicle *w2 = w; - Vehicle *v2 = v; - do { - if (v2->cargo_type != w2->cargo_type || v2->cargo_subtype != w2->cargo_subtype) { - /* We can't pay for refitting because we can't estimate refitting costs for a vehicle before it's build. - * If we pay for it anyway, the cost and the estimated cost will not be the same and we will have an assert. - * We need to check the whole chain if it is a train because some newgrf articulated engines can refit some units only (and not the front) */ - total_cost += DoCommand(0, w->index, v2->cargo_type | (v2->cargo_subtype << 8), flags, GetCmdRefitVeh(v)); - break; // We learned that the engine in question needed a refit. No need to check anymore - } - } while (v->type == VEH_TRAIN && (w2 = w2->next) != NULL && (v2 = v2->next) != NULL); - if (v->type == VEH_TRAIN && HASBIT(v->u.rail.flags, VRF_REVERSE_DIRECTION)) { SETBIT(w->u.rail.flags, VRF_REVERSE_DIRECTION); } @@ -1904,6 +1880,53 @@ int32 CmdCloneVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) _new_vehicle_id = w_front->index; } + /* Take care of refitting. */ + w = w_front; + v = v_front; + + /* Both building and refitting are influenced by newgrf callbacks, which + * makes it impossible to accurately estimate the cloning costs. In + * particular, it is possible for engines of the same type to be built with + * different numbers of articulated parts, so when refitting we have to + * loop over real vehicles first, and then the articulated parts of those + * vehicles in a different loop. */ + do { + do { + if (flags & DC_EXEC) { + assert(w != NULL); + + if (w->cargo_type != v->cargo_type || w->cargo_subtype != v->cargo_type) { + cost = DoCommand(0, w->index, v->cargo_type | (v->cargo_subtype << 8) | 1U << 16 , flags, GetCmdRefitVeh(v)); + if (!CmdFailed(cost)) total_cost += cost; + } + + if (w->type == VEH_TRAIN && EngineHasArticPart(w)) { + w = GetNextArticPart(w); + } else { + break; + } + } else { + CargoID initial_cargo = GetEngineCargoType(v->engine_type); + + if (v->cargo_type != initial_cargo && initial_cargo != CT_INVALID) { + total_cost += GetRefitCost(v->engine_type); + } + } + } while (v->type == VEH_TRAIN && EngineHasArticPart(v) && (v = GetNextArticPart(v)) != NULL); + + if (flags & DC_EXEC) w = GetNextVehicle(w); + } while (v->type == VEH_TRAIN && (v = GetNextVehicle(v)) != NULL); + + /* Since we can't estimate the cost of cloning a vehicle accurately we must + * check whether the player has enough money manually. */ + if (!CheckPlayerHasMoney(total_cost)) { + if (flags & DC_EXEC) { + /* The vehicle has already been bought, so now it must be sold again. */ + DoCommand(w_front->tile, w_front->index, 1, flags, GetCmdSellVeh(w_front)); + } + return CMD_ERROR; + } + /* Set the expense type last as refitting will make the cost go towards * running costs... */ SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);