diff --git a/src/command.cpp b/src/command.cpp index 4aa6c367a1..0bed5b65f6 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -492,7 +492,7 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdStartStopVehicle, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_START_STOP_VEHICLE DEF_CMD(CmdMassStartStopVehicle, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_MASS_START_STOP DEF_CMD(CmdAutoreplaceVehicle, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_AUTOREPLACE_VEHICLE - DEF_CMD(CmdTemplateReplaceVehicle, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_TEMPLATE_REPLACE_VEHICLE + DEF_CMD(CmdTemplateReplaceVehicle, CMD_NO_TEST, CMDT_VEHICLE_MANAGEMENT ), // CMD_TEMPLATE_REPLACE_VEHICLE DEF_CMD(CmdDepotSellAllVehicles, 0, CMDT_VEHICLE_CONSTRUCTION ), // CMD_DEPOT_SELL_ALL_VEHICLES DEF_CMD(CmdDepotMassAutoReplace, 0, CMDT_VEHICLE_CONSTRUCTION ), // CMD_DEPOT_MASS_AUTOREPLACE DEF_CMD(CmdCreateGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_CREATE_GROUP diff --git a/src/saveload/tbtr_template_veh_sl.cpp b/src/saveload/tbtr_template_veh_sl.cpp index de6af959fd..5deb72e4bf 100644 --- a/src/saveload/tbtr_template_veh_sl.cpp +++ b/src/saveload/tbtr_template_veh_sl.cpp @@ -68,7 +68,7 @@ static void Load_TMPLS() int index; while ((index = SlIterateArray()) != -1) { - TemplateVehicle *tv = new (index) TemplateVehicle(); //TODO:check with veh sl code + TemplateVehicle *tv = new (index) TemplateVehicle(); SlObject(tv, GTD()); } } diff --git a/src/tbtr_template_gui_main.cpp b/src/tbtr_template_gui_main.cpp index 5922f2f912..2f5985b6c4 100644 --- a/src/tbtr_template_gui_main.cpp +++ b/src/tbtr_template_gui_main.cpp @@ -658,7 +658,7 @@ public: /* Draw the number of trains that still need to be treated by the currently selected template replacement */ if (tid != INVALID_TEMPLATE) { const TemplateVehicle *tv = TemplateVehicle::Get(tid); - const int num_trains = NumTrainsNeedTemplateReplacement(g_id, tv); + const uint num_trains = CountsTrainsNeedingTemplateReplacement(g_id, tv); // Draw number SetDParam(0, num_trains); int inner_right = DrawString(col2 + ScaleGUITrad(4), right - ScaleGUITrad(4), text_y, STR_JUST_INT, num_trains ? TC_ORANGE : TC_GREY, SA_RIGHT); diff --git a/src/tbtr_template_vehicle.cpp b/src/tbtr_template_vehicle.cpp index 7be2d4002c..a130e1a02c 100644 --- a/src/tbtr_template_vehicle.cpp +++ b/src/tbtr_template_vehicle.cpp @@ -68,9 +68,9 @@ void TemplateVehicleImageDimensions::SetFromTrain(const Train *t) } } -TemplateVehicle::TemplateVehicle(VehicleType ty, EngineID eid, byte subtypeflag, Owner current_owner) +TemplateVehicle::TemplateVehicle(VehicleType type, EngineID eid, Owner current_owner) { - this->type = ty; + this->type = type; this->engine_type = eid; this->reuse_depot_vehicles = false; diff --git a/src/tbtr_template_vehicle.h b/src/tbtr_template_vehicle.h index f8e7550dc1..be9e792529 100644 --- a/src/tbtr_template_vehicle.h +++ b/src/tbtr_template_vehicle.h @@ -47,7 +47,7 @@ extern TemplatePool _template_pool; extern bool _template_vehicle_images_valid; /// listing/sorting templates -typedef GUIList GUITemplateList; +typedef GUIList GUITemplateList; struct TemplateVehicleImageDimensions { int reference_width; @@ -126,7 +126,7 @@ public: TemplateVehicleImageDimensions image_dimensions; ///< NOSAVE: image dimensions SpriteID colourmap; ///< NOSAVE: cached colour mapping - TemplateVehicle(VehicleType type = VEH_INVALID, EngineID e = INVALID_ENGINE, byte B = 0, Owner = _local_company); + TemplateVehicle(VehicleType type = VEH_INVALID, EngineID e = INVALID_ENGINE, Owner = _local_company); TemplateVehicle(EngineID eid) { @@ -143,16 +143,16 @@ public: ~TemplateVehicle(); - inline TemplateVehicle* Next() const { return this->next; } - inline TemplateVehicle* Prev() const { return this->previous; } - inline TemplateVehicle* First() const { return this->first; } + inline TemplateVehicle *Next() const { return this->next; } + inline TemplateVehicle *Prev() const { return this->previous; } + inline TemplateVehicle *First() const { return this->first; } - void SetNext(TemplateVehicle*); - void SetPrev(TemplateVehicle*); - void SetFirst(TemplateVehicle*); + void SetNext(TemplateVehicle *v); + void SetPrev(TemplateVehicle *v); + void SetFirst(TemplateVehicle *v); - TemplateVehicle* GetNextUnit() const; - TemplateVehicle* GetPrevUnit(); + TemplateVehicle *GetNextUnit() const; + TemplateVehicle *GetPrevUnit(); bool IsSetReuseDepotVehicles() const { return this->reuse_depot_vehicles; } bool IsSetKeepRemainingVehicles() const { return this->keep_remaining_vehicles; } @@ -176,7 +176,6 @@ public: inline bool IsFreeWagonChain() const { return HasBit(this->subtype, GVSF_FREE_WAGON); } - // since CmdBuildTemplateVehicle(...) inline void SetFrontEngine() { SetBit(this->subtype, GVSF_FRONT); } inline void SetEngine() { SetBit(this->subtype, GVSF_ENGINE); } inline void SetArticulatedPart() { SetBit(this->subtype, GVSF_ARTICULATED_PART); } @@ -206,7 +205,7 @@ struct TemplateReplacement : TemplateReplacementPool::PoolItem<&_template_replac GroupID group; TemplateID sel_template; - TemplateReplacement(GroupID gid, TemplateID tid) { this->group=gid; this->sel_template=tid; } + TemplateReplacement(GroupID gid, TemplateID tid) { this->group = gid; this->sel_template = tid; } TemplateReplacement() {} ~TemplateReplacement(); @@ -221,12 +220,12 @@ struct TemplateReplacement : TemplateReplacementPool::PoolItem<&_template_replac static void PreCleanPool(); }; -TemplateReplacement* GetTemplateReplacementByGroupID(GroupID); -TemplateID GetTemplateIDByGroupID(GroupID); -TemplateID GetTemplateIDByGroupIDRecursive(GroupID); -bool IssueTemplateReplacement(GroupID, TemplateID); +TemplateReplacement *GetTemplateReplacementByGroupID(GroupID gid); +TemplateID GetTemplateIDByGroupID(GroupID gid); +TemplateID GetTemplateIDByGroupIDRecursive(GroupID gid); +bool IssueTemplateReplacement(GroupID gid, TemplateID tid); -uint DeleteTemplateReplacementsByGroupID(GroupID); +uint DeleteTemplateReplacementsByGroupID(GroupID g_id); void ReindexTemplateReplacements(); diff --git a/src/tbtr_template_vehicle_func.cpp b/src/tbtr_template_vehicle_func.cpp index be0a820266..9c7ebbc082 100644 --- a/src/tbtr_template_vehicle_func.cpp +++ b/src/tbtr_template_vehicle_func.cpp @@ -45,48 +45,6 @@ bool _template_vehicle_images_valid = false; -#ifdef _DEBUG -// debugging printing functions for convenience, usually called from gdb -void tbtr_debug_pat() -{ - for (TemplateVehicle *tv : TemplateVehicle::Iterate()) { - if (tv->Prev()) continue; - tbtr_debug_ptv(tv); - printf("__________\n"); - } -} - -void tbtr_debug_pav() -{ - for (Train *t : Train::Iterate()) { - if (t->Previous()) continue; - tbtr_debug_pvt(t); - printf("__________\n"); - } -} - -void tbtr_debug_ptv(TemplateVehicle* tv) -{ - if (!tv) return; - while (tv->Next() ) { - printf("eid:%3d st:%2d tv:%p next:%p cargo: %d cargo_sub: %d\n", tv->engine_type, tv->subtype, tv, tv->Next(), tv->cargo_type, tv->cargo_subtype); - tv = tv->Next(); - } - printf("eid:%3d st:%2d tv:%p next:%p cargo: %d cargo_sub: %d\n", tv->engine_type, tv->subtype, tv, tv->Next(), tv->cargo_type, tv->cargo_subtype); -} - -void tbtr_debug_pvt (const Train *printme) -{ - for (const Train *tmp = printme; tmp; tmp = tmp->Next()) { - if (tmp->index <= 0) { - printf("train has weird index: %d %d %p\n", tmp->index, tmp->engine_type, tmp); - return; - } - printf("eid:%3d index:%2d subtype:%2d vehstat: %d cargo_t: %d cargo_sub: %d ref:%p\n", tmp->engine_type, tmp->index, tmp->subtype, tmp->vehstatus, tmp->cargo_type, tmp->cargo_subtype, tmp); - } -} -#endif - void BuildTemplateGuiList(GUITemplateList *list, Scrollbar *vscroll, Owner oid, RailType railtype) { list->clear(); @@ -204,14 +162,14 @@ TemplateVehicle* TemplateVehicleFromVirtualTrain(Train *virt) } // forward declaration, defined in train_cmd.cpp -CommandCost CmdSellRailWagon(DoCommandFlag, Vehicle*, uint16, uint32); +CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, uint16 data, uint32 user); -Train* DeleteVirtualTrain(Train *chain, Train *to_del) { +Train *DeleteVirtualTrain(Train *chain, Train *to_del) +{ if (chain != to_del) { CmdSellRailWagon(DC_EXEC, to_del, 0, 0); return chain; - } - else { + } else { chain = chain->GetNextUnit(); CmdSellRailWagon(DC_EXEC, to_del, 0, 0); return chain; @@ -219,13 +177,15 @@ Train* DeleteVirtualTrain(Train *chain, Train *to_del) { } // retrieve template vehicle from template replacement that belongs to the given group -TemplateVehicle* GetTemplateVehicleByGroupID(GroupID gid) { +TemplateVehicle *GetTemplateVehicleByGroupID(GroupID gid) +{ if (gid >= NEW_GROUP) return nullptr; const TemplateID tid = GetTemplateIDByGroupID(gid); return tid != INVALID_TEMPLATE ? TemplateVehicle::GetIfValid(tid) : nullptr; } -TemplateVehicle* GetTemplateVehicleByGroupIDRecursive(GroupID gid) { +TemplateVehicle *GetTemplateVehicleByGroupIDRecursive(GroupID gid) +{ if (gid >= NEW_GROUP) return nullptr; const TemplateID tid = GetTemplateIDByGroupIDRecursive(gid); return tid != INVALID_TEMPLATE ? TemplateVehicle::GetIfValid(tid) : nullptr; @@ -258,30 +218,19 @@ bool TemplateVehicleContainsEngineOfRailtype(const TemplateVehicle *tv, RailType return false; } -//helper -bool ChainContainsVehicle(Train *chain, Train *mem) +Train *ChainContainsEngine(EngineID eid, Train *chain) { - for (; chain; chain = chain->Next()) { - if (chain == mem) { - return true; - } + for (; chain != nullptr; chain = chain->GetNextUnit()) { + if (chain->engine_type == eid) return chain; } - return false; -} - -// has O(n) -Train* ChainContainsEngine(EngineID eid, Train *chain) { - for (; chain; chain=chain->GetNextUnit()) - if (chain->engine_type == eid) - return chain; return nullptr; } static bool IsTrainUsableAsTemplateReplacementSource(const Train *t) { - if (t->IsFreeWagon()) return true; + if (t->First()->IsFreeWagon()) return true; - if (t->IsPrimaryVehicle() && t->IsStoppedInDepot()) { + if (t->IsPrimaryVehicle() && t->IsStoppedInDepot() && t->GetNextUnit() == nullptr) { if (t->GetNumOrders() != 0) return false; if (t->IsOrderListShared()) return false; if (t->group_id != DEFAULT_GROUP) return false; @@ -291,18 +240,33 @@ static bool IsTrainUsableAsTemplateReplacementSource(const Train *t) return false; } -// has O(n^2) -Train* DepotContainsEngine(TileIndex tile, EngineID eid, Train *not_in = nullptr) +void TemplateDepotVehicles::Init(TileIndex tile) { - for (Train *t : Train::Iterate()) { + FindVehicleOnPos(tile, VEH_TRAIN, this, [](Vehicle *v, void *data) -> Vehicle * { + TemplateDepotVehicles *self = static_cast(data); + self->vehicles.insert(v->index); + return v; + }); +} + +void TemplateDepotVehicles::RemoveVehicle(VehicleID id) +{ + this->vehicles.erase(id); +} + +Train *TemplateDepotVehicles::ContainsEngine(EngineID eid, Train *not_in) +{ + for (VehicleID id : this->vehicles) { + Train *t = Train::GetIfValid(id); + if (t == nullptr) continue; // conditions: v is stopped in the given depot, has the right engine and if 'not_in' is given v must not be contained within 'not_in' // if 'not_in' is nullptr, no check is needed - if (t->tile == tile + if (t->owner == _current_company // If the veh belongs to a chain, wagons will not return true on IsStoppedInDepot(), only primary vehicles will // in case of t not a primary veh, we demand it to be a free wagon to consider it for replacement && IsTrainUsableAsTemplateReplacementSource(t) && t->engine_type == eid - && (not_in == nullptr || ChainContainsVehicle(not_in, t) == false)) { + && (not_in == nullptr || not_in->First() != t->First())) { return t; } } @@ -370,9 +334,9 @@ void CopyWagonStatus(TemplateVehicle *from, Train *to) to->cargo_subtype = from->cargo_subtype; } -int NumTrainsNeedTemplateReplacement(GroupID g_id, const TemplateVehicle *tv) +uint CountsTrainsNeedingTemplateReplacement(GroupID g_id, const TemplateVehicle *tv) { - int count = 0; + uint count = 0; if (!tv) return count; for (Train *t : Train::Iterate()) { @@ -382,6 +346,7 @@ int NumTrainsNeedTemplateReplacement(GroupID g_id, const TemplateVehicle *tv) } return count; } + // refit each vehicle in t as is in tv, assume t and tv contain the same types of vehicles CommandCost CmdRefitTrainFromTemplate(Train *t, TemplateVehicle *tv, DoCommandFlag flags) { @@ -431,14 +396,14 @@ void TransferCargoForTrain(Train *old_veh, Train *new_head) { assert(new_head->IsPrimaryVehicle() || new_head->IsFreeWagon()); - CargoID _cargo_type = old_veh->cargo_type; - byte _cargo_subtype = old_veh->cargo_subtype; + const CargoID cargo_type = old_veh->cargo_type; + const byte cargo_subtype = old_veh->cargo_subtype; // how much cargo has to be moved (if possible) uint remainingAmount = old_veh->cargo.TotalCount(); // each vehicle in the new chain shall be given as much of the old cargo as possible, until none is left for (Train *tmp = new_head; tmp != nullptr && remainingAmount > 0; tmp = tmp->GetNextUnit()) { - if (tmp->cargo_type == _cargo_type && tmp->cargo_subtype == _cargo_subtype) { + if (tmp->cargo_type == cargo_type && tmp->cargo_subtype == cargo_subtype) { // calculate the free space for new cargo on the current vehicle uint curCap = tmp->cargo_cap - tmp->cargo.TotalCount(); uint moveAmount = std::min(remainingAmount, curCap); @@ -450,11 +415,6 @@ void TransferCargoForTrain(Train *old_veh, Train *new_head) } } - // TODO: needs to be implemented, too - // // from autoreplace_cmd.cpp : 121 - /* Any left-overs will be thrown away, but not their feeder share. */ - //if (src->cargo_cap < src->cargo.TotalCount()) src->cargo.Truncate(src->cargo.TotalCount() - src->cargo_cap); - /* Update train weight etc., the old vehicle will be sold anyway */ new_head->ConsistChanged(CCF_LOADUNLOAD); } @@ -506,8 +466,7 @@ int GetTemplateVehicleEstimatedMaxAchievableSpeed(const TemplateVehicle *tv, int if (mass < 1) mass = 1; - do - { + do { max_speed++; acceleration = GetTrainRealisticAccelerationAtSpeed(max_speed, mass, tv->power, tv->max_te, tv->air_drag, tv->railtype); } while (acceleration > 0 && max_speed < speed_cap); diff --git a/src/tbtr_template_vehicle_func.h b/src/tbtr_template_vehicle_func.h index 9f8513de7b..df8aeb3763 100644 --- a/src/tbtr_template_vehicle_func.h +++ b/src/tbtr_template_vehicle_func.h @@ -12,8 +12,8 @@ #include "stdafx.h" #include "window_gui.h" - #include "tbtr_template_vehicle.h" +#include "3rdparty/cpp-btree/btree_set.h" Train* VirtualTrainFromTemplateVehicle(const TemplateVehicle* tv, StringID &err, uint32 user); @@ -30,30 +30,28 @@ void SetupTemplateVehicleFromVirtual(TemplateVehicle *tmp, TemplateVehicle *prev CommandCost CmdTemplateReplaceVehicle(Train*, bool, DoCommandFlag); -#ifdef _DEBUG -// for testing -void tbtr_debug_pat(); -void tbtr_debug_pav(); -void tbtr_debug_ptv(TemplateVehicle*); -void tbtr_debug_pvt(const Train*); -#endif +TemplateVehicle *GetTemplateVehicleByGroupID(GroupID gid); +TemplateVehicle *GetTemplateVehicleByGroupIDRecursive(GroupID gid); +Train *ChainContainsEngine(EngineID eid, Train *chain); -TemplateVehicle* GetTemplateVehicleByGroupID(GroupID); -TemplateVehicle* GetTemplateVehicleByGroupIDRecursive(GroupID); -bool ChainContainsVehicle(Train*, Train*); -Train* ChainContainsEngine(EngineID, Train*); -Train* DepotContainsEngine(TileIndex, EngineID, Train*); +struct TemplateDepotVehicles { + btree::btree_set vehicles; -int NumTrainsNeedTemplateReplacement(GroupID, const TemplateVehicle*); + void Init(TileIndex tile); + void RemoveVehicle(VehicleID id); + Train* ContainsEngine(EngineID eid, Train *not_in); +}; + +uint CountsTrainsNeedingTemplateReplacement(GroupID g_id, const TemplateVehicle *tv); CommandCost TestBuyAllTemplateVehiclesInChain(TemplateVehicle *tv, TileIndex tile); -CommandCost CmdRefitTrainFromTemplate(Train *t, TemplateVehicle *tv, DoCommandFlag); +CommandCost CmdRefitTrainFromTemplate(Train *t, TemplateVehicle *tv, DoCommandFlag flags); void BreakUpRemainders(Train *t); -bool TemplateVehicleContainsEngineOfRailtype(const TemplateVehicle*, RailType); +bool TemplateVehicleContainsEngineOfRailtype(const TemplateVehicle *tv, RailType type); -void TransferCargoForTrain(Train*, Train*); +void TransferCargoForTrain(Train *old_veh, Train *new_head); void NeutralizeStatus(Train *t); diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 495972ab70..eef3b240ef 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -7075,51 +7075,42 @@ void ClearVehicleWindows(const Train *v) */ CommandCost CmdTemplateReplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { - VehicleID vehicle_id = p1; + Train *incoming = Train::GetIfValid(p1); - Vehicle* vehicle = Vehicle::GetIfValid(vehicle_id); - - if (vehicle == nullptr || vehicle->type != VEH_TRAIN) { + if (incoming == nullptr) { return CMD_ERROR; } - bool should_execute = (flags & DC_EXEC) != 0; - - if (!should_execute) { + if (!(flags & DC_EXEC)) { return CommandCost(); } - Train* incoming = Train::From(vehicle); - bool leaveDepot = (p2 != 0); + const bool leave_depot = (p2 != 0); + auto guard = scope_guard([&]() { + _new_vehicle_id = incoming->index; + if (leave_depot) incoming->vehstatus &= ~VS_STOPPED; + }); Train *new_chain = nullptr; Train *remainder_chain = nullptr; - Train *tmp_chain = nullptr; TemplateVehicle *tv = GetTemplateVehicleByGroupIDRecursive(incoming->group_id); if (tv == nullptr) { - if (leaveDepot) incoming->vehstatus &= ~VS_STOPPED; return CMD_ERROR; } EngineID eid = tv->engine_type; - _new_vehicle_id = p1; - - CommandCost buy(EXPENSES_NEW_VEHICLES); - CommandCost move_cost(EXPENSES_NEW_VEHICLES); CommandCost tmp_result(EXPENSES_NEW_VEHICLES); - /* first some tests on necessity and sanity */ - if (tv == nullptr) return buy; - if (tv->IsReplaceOldOnly() && !vehicle->NeedsAutorenewing(Company::Get(vehicle->owner), false)) { - if (leaveDepot) incoming->vehstatus &= ~VS_STOPPED; - return buy; + if (tv == nullptr) return CommandCost(); + if (tv->IsReplaceOldOnly() && !incoming->NeedsAutorenewing(Company::Get(incoming->owner), false)) { + return CommandCost(); } bool need_replacement = !TrainMatchesTemplate(incoming, tv); bool need_refit = !TrainMatchesTemplateRefit(incoming, tv); bool use_refit = tv->refit_as_template; CargoID store_refit_ct = CT_INVALID; - short store_refit_csubt = 0; + uint16 store_refit_csubt = 0; // if a train shall keep its old refit, store the refit setting of its first vehicle if (!use_refit) { for (Train *getc = incoming; getc != nullptr; getc = getc->GetNextUnit()) { @@ -7130,30 +7121,26 @@ CommandCost CmdTemplateReplaceVehicle(TileIndex tile, DoCommandFlag flags, uint3 } } - // TODO: set result status to success/no success before returning if (!need_replacement) { if (!need_refit || !use_refit) { - /* before returning, release incoming train first if 2nd param says so */ - if (leaveDepot) incoming->vehstatus &= ~VS_STOPPED; - return buy; + return CommandCost(); } } else { - CommandCost buyCost = TestBuyAllTemplateVehiclesInChain(tv, tile); - if (!buyCost.Succeeded()) { - if (leaveDepot) incoming->vehstatus &= ~VS_STOPPED; - if (buyCost.GetErrorMessage() == INVALID_STRING_ID) return_cmd_error(STR_ERROR_CAN_T_BUY_TRAIN); - return buyCost; - } else if (!CheckCompanyHasMoney(buyCost)) { - if (leaveDepot) incoming->vehstatus &= ~VS_STOPPED; - return_cmd_error(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY); + CommandCost buy_cost = TestBuyAllTemplateVehiclesInChain(tv, tile); + if (buy_cost.Failed()) { + if (buy_cost.GetErrorMessage() == INVALID_STRING_ID) return CommandCost(STR_ERROR_CAN_T_BUY_TRAIN); + return buy_cost; + } else if (!CheckCompanyHasMoney(buy_cost)) { + return CommandCost(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY); } } if (need_replacement || (need_refit && use_refit)) RegisterGameEvents(GEF_TBTR_REPLACEMENT); - /* define replacement behavior */ - bool reuseDepot = tv->IsSetReuseDepotVehicles(); - bool keepRemainders = tv->IsSetKeepRemainingVehicles(); + TemplateDepotVehicles depot_vehicles; + if (tv->IsSetReuseDepotVehicles()) depot_vehicles.Init(tile); + + CommandCost buy(EXPENSES_NEW_VEHICLES); if (need_replacement) { // step 1: generate primary for newchain and generate remainder_chain @@ -7163,46 +7150,70 @@ CommandCost CmdTemplateReplaceVehicle(TileIndex tile, DoCommandFlag flags, uint3 // 3. primary might be available as orphan vehicle in the depot // 4. we need to buy a new engine for the primary // all options other than 1. need to make sure to copy incoming's primary's status - if (eid == incoming->engine_type) { // 1 - new_chain = incoming; - remainder_chain = incoming->GetNextUnit(); - if (remainder_chain) { - move_cost.AddCost(CmdMoveRailVehicle(tile, flags, remainder_chain->index | (1 << 20), INVALID_VEHICLE, 0)); + auto setup_head = [&]() -> CommandCost { + /* Case 1 */ + if (eid == incoming->engine_type) { + new_chain = incoming; + remainder_chain = incoming->GetNextUnit(); + if (remainder_chain) { + CommandCost move_cost = CmdMoveRailVehicle(tile, flags, remainder_chain->index | (1 << 20), INVALID_VEHICLE, 0); + if (move_cost.Failed()) { + /* This should not fail, if it does give up immediately */ + return move_cost; + } + } + return CommandCost(); } - } else if ((tmp_chain = ChainContainsEngine(eid, incoming)) && tmp_chain != nullptr) { // 2 - // new_chain is the needed engine, move it to an empty spot in the depot - new_chain = tmp_chain; - if (flags & DC_EXEC) ClearVehicleWindows(tmp_chain); - move_cost.AddCost(DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags, CMD_MOVE_RAIL_VEHICLE)); - remainder_chain = incoming; - } else if (reuseDepot && (tmp_chain = DepotContainsEngine(tile, eid, incoming)) && tmp_chain != nullptr) { // 3 - new_chain = tmp_chain; - if (flags & DC_EXEC) ClearVehicleWindows(tmp_chain); - move_cost.AddCost(DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags, CMD_MOVE_RAIL_VEHICLE)); - remainder_chain = incoming; - } else { // 4 - tmp_result = DoCommand(tile, eid, 0, flags, CMD_BUILD_VEHICLE); + + /* Case 2 */ + new_chain = ChainContainsEngine(eid, incoming); + if (new_chain != nullptr) { + /* new_chain is the needed engine, move it to an empty spot in the depot */ + CommandCost move_cost = DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags, CMD_MOVE_RAIL_VEHICLE); + if (move_cost.Succeeded()) { + remainder_chain = incoming; + return CommandCost(); + } + } + + /* Case 3 */ + if (tv->IsSetReuseDepotVehicles()) { + new_chain = depot_vehicles.ContainsEngine(eid, incoming); + if (new_chain != nullptr) { + ClearVehicleWindows(new_chain); + CommandCost move_cost = DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags, CMD_MOVE_RAIL_VEHICLE); + if (move_cost.Succeeded()) { + depot_vehicles.RemoveVehicle(new_chain->index); + remainder_chain = incoming; + return CommandCost(); + } + } + } + + /* Case 4 */ + CommandCost buy_cost = DoCommand(tile, eid | (1 << 16), 0, flags, CMD_BUILD_VEHICLE); /* break up in case buying the vehicle didn't succeed */ - if (!tmp_result.Succeeded()) { - return tmp_result; + if (buy_cost.Failed()) { + return buy_cost; } - buy.AddCost(tmp_result); + buy.AddCost(buy_cost); new_chain = Train::Get(_new_vehicle_id); - /* make sure the newly built engine is not attached to any free wagons inside the depot */ - move_cost.AddCost(DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags, CMD_MOVE_RAIL_VEHICLE)); /* prepare the remainder chain */ remainder_chain = incoming; - } + return CommandCost(); + }; + CommandCost head_result = setup_head(); + if (head_result.Failed()) return head_result; + // If we bought a new engine or reused one from the depot, copy some parameters from the incoming primary engine - if (incoming != new_chain && flags == DC_EXEC) { + if (incoming != new_chain) { CopyHeadSpecificThings(incoming, new_chain, flags); NeutralizeStatus(incoming); // additionally, if we don't want to use the template refit, refit as incoming // the template refit will be set further down, if we use it at all if (!use_refit) { - uint32 cb = GetCmdRefitVeh(new_chain); - DoCommand(new_chain->tile, new_chain->index, store_refit_ct | store_refit_csubt << 8 | (1 << 16) | (1 << 31), flags, cb); + buy.AddCost(DoCommand(new_chain->tile, new_chain->index, store_refit_ct | store_refit_csubt << 8 | (1 << 16) | (1 << 31), flags, GetCmdRefitVeh(new_chain))); } } @@ -7211,55 +7222,76 @@ CommandCost CmdTemplateReplaceVehicle(TileIndex tile, DoCommandFlag flags, uint3 // 1. needed engine might be within remainder_chain already // 2. needed engine might be orphaned within the depot (copy status) // 3. we need to buy (again) (copy status) - TemplateVehicle *cur_tmpl = tv->GetNextUnit(); Train *last_veh = new_chain; - while (cur_tmpl) { - // 1. engine contained in remainder chain - if ((tmp_chain = ChainContainsEngine(cur_tmpl->engine_type, remainder_chain)) && tmp_chain != nullptr) { - // advance remainder_chain (if necessary) to not lose track of it - if (tmp_chain == remainder_chain) { - remainder_chain = remainder_chain->GetNextUnit(); + for (TemplateVehicle *cur_tmpl = tv->GetNextUnit(); cur_tmpl != nullptr; cur_tmpl = cur_tmpl->GetNextUnit()) { + Train *new_part = nullptr; + auto setup_chain_part = [&]() { + /* Case 1: engine contained in remainder chain */ + new_part = ChainContainsEngine(cur_tmpl->engine_type, remainder_chain); + if (new_part != nullptr) { + Train *remainder_chain_next = remainder_chain; + if (new_part == remainder_chain) { + remainder_chain_next = remainder_chain->GetNextUnit(); + } + CommandCost move_cost = CmdMoveRailVehicle(tile, flags, new_part->index, last_veh->index, 0); + if (move_cost.Succeeded()) { + remainder_chain = remainder_chain_next; + return; + } } - move_cost.AddCost(CmdMoveRailVehicle(tile, flags, tmp_chain->index, last_veh->index, 0)); - } - // 2. engine contained somewhere else in the depot - else if (reuseDepot && (tmp_chain = DepotContainsEngine(tile, cur_tmpl->engine_type, new_chain)) && tmp_chain != nullptr) { - move_cost.AddCost(CmdMoveRailVehicle(tile, flags, tmp_chain->index, last_veh->index, 0)); - } - // 3. must buy new engine - else { - tmp_result = DoCommand(tile, cur_tmpl->engine_type, 0, flags, CMD_BUILD_VEHICLE); - if (!tmp_result.Succeeded()) { - return tmp_result; + + /* Case 2: engine contained somewhere else in the depot */ + if (tv->IsSetReuseDepotVehicles()) { + new_part = depot_vehicles.ContainsEngine(cur_tmpl->engine_type, new_chain); + if (new_part != nullptr) { + CommandCost move_cost = CmdMoveRailVehicle(tile, flags, new_part->index, last_veh->index, 0); + if (move_cost.Succeeded()) { + depot_vehicles.RemoveVehicle(new_part->index); + return; + } + } } - buy.AddCost(tmp_result); - tmp_chain = Train::Get(_new_vehicle_id); - move_cost.AddCost(CmdMoveRailVehicle(tile, flags, tmp_chain->index, last_veh->index, 0)); + + /* Case 3: must buy new engine */ + CommandCost buy_cost = DoCommand(tile, cur_tmpl->engine_type | (1 << 16), 0, flags, CMD_BUILD_VEHICLE); + if (buy_cost.Failed()) { + new_part = nullptr; + return; + } + new_part = Train::Get(_new_vehicle_id); + CommandCost move_cost = CmdMoveRailVehicle(tile, flags, new_part->index, last_veh->index, 0); + if (move_cost.Succeeded()) { + buy.AddCost(buy_cost); + } else { + DoCommand(tile, new_part->index, 0, flags, CMD_SELL_VEHICLE); + new_part = nullptr; + } + }; + setup_chain_part(); + if (new_part != nullptr) { + last_veh = new_part; } // TODO: is this enough ? might it be that we bought a new wagon here and it now has std refit ? - if (need_refit && flags == DC_EXEC) { + if (need_refit && new_part != nullptr) { if (use_refit) { - uint32 cb = GetCmdRefitVeh(tmp_chain); - DoCommand(tmp_chain->tile, tmp_chain->index, cur_tmpl->cargo_type | (cur_tmpl->cargo_subtype << 8) | (1 << 16) | (1 << 31), flags, cb); + DoCommand(tile, new_part->index, cur_tmpl->cargo_type | (cur_tmpl->cargo_subtype << 8) | (1 << 16) | (1 << 31), flags, GetCmdRefitVeh(new_part)); } else { - uint32 cb = GetCmdRefitVeh(tmp_chain); - DoCommand(tmp_chain->tile, tmp_chain->index, store_refit_ct | (store_refit_csubt << 8) | (1 << 16) | (1 << 31), flags, cb); + DoCommand(tile, new_part->index, store_refit_ct | (store_refit_csubt << 8) | (1 << 16) | (1 << 31), flags, GetCmdRefitVeh(new_part)); } - if (HasBit(tmp_chain->flags, VRF_REVERSE_DIRECTION) != HasBit(cur_tmpl->ctrl_flags, TVCF_REVERSED)) { - DoCommand(tmp_chain->tile, tmp_chain->index, true, flags, CMD_REVERSE_TRAIN_DIRECTION | CMD_MSG(STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE)); + if (HasBit(new_part->flags, VRF_REVERSE_DIRECTION) != HasBit(cur_tmpl->ctrl_flags, TVCF_REVERSED)) { + DoCommand(tile, new_part->index, true, flags, CMD_REVERSE_TRAIN_DIRECTION | CMD_MSG(STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE)); } } - cur_tmpl = cur_tmpl->GetNextUnit(); - last_veh = tmp_chain; + } - } - /* no replacement done */ - else { + } else { + /* no replacement done */ new_chain = incoming; } + /// step 3: reorder and neutralize the remaining vehicles from incoming - // wagons remaining from remainder_chain should be filled up in as few freewagonchains as possible - // each locos might be left as singular in the depot + // wagons remaining from remainder_chain should be filled up in as few free wagon chains as possible + // each loco might be left as singular in the depot // neutralize each remaining engine's status // refit, only if the template option is set so @@ -7268,18 +7300,15 @@ CommandCost CmdTemplateReplaceVehicle(TileIndex tile, DoCommandFlag flags, uint3 } if (new_chain && remainder_chain) { - for (Train *ct = remainder_chain; ct; ct = ct->GetNextUnit()) { + for (Train *ct = remainder_chain; ct != nullptr; ct = ct->Next()) { TransferCargoForTrain(ct, new_chain); } } - // point incoming to the newly created train so that starting/stopping from the calling function can be done + // point incoming to the newly created train so that starting/stopping affects the replacement train incoming = new_chain; - if (leaveDepot && flags == DC_EXEC) { - new_chain->vehstatus &= ~VS_STOPPED; - } - if (remainder_chain && keepRemainders && flags == DC_EXEC) { + if (remainder_chain && tv->IsSetKeepRemainingVehicles()) { BreakUpRemainders(remainder_chain); } else if (remainder_chain) { buy.AddCost(DoCommand(tile, remainder_chain->index | (1 << 20), 0, flags, CMD_SELL_VEHICLE)); @@ -7288,8 +7317,6 @@ CommandCost CmdTemplateReplaceVehicle(TileIndex tile, DoCommandFlag flags, uint3 /* Redraw main gui for changed statistics */ SetWindowClassesDirty(WC_TEMPLATEGUI_MAIN); - _new_vehicle_id = new_chain->index; - return buy; } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index fe8f76d3ca..b689aa2ead 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1638,12 +1638,14 @@ void CallVehicleTicks() tmpl_cur_company.Change(t->owner); + _new_vehicle_id = INVALID_VEHICLE; + t->vehstatus |= VS_STOPPED; CommandCost res = DoCommand(t->tile, t->index, leaveDepot ? 1 : 0, DC_EXEC, CMD_TEMPLATE_REPLACE_VEHICLE); - if (res.Succeeded()) { + if (_new_vehicle_id != INVALID_VEHICLE) { VehicleID t_new = _new_vehicle_id; - t = Train::From(Vehicle::Get(t_new)); + t = Train::Get(t_new); const Company *c = Company::Get(_current_company); SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money)); CommandCost res2 = DoCommand(0, t_new, 1, DC_EXEC, CMD_AUTOREPLACE_VEHICLE); @@ -1653,14 +1655,13 @@ void CallVehicleTicks() if (!IsLocalCompany()) continue; - if (res.Succeeded()) { - if (res.GetCost() != 0) { - ShowCostOrIncomeAnimation(x, y, z, res.GetCost()); - } - continue; + if (res.GetCost() != 0) { + ShowCostOrIncomeAnimation(x, y, z, res.GetCost()); } - ShowAutoReplaceAdviceMessage(res, t); + if (res.Failed()) { + ShowAutoReplaceAdviceMessage(res, t); + } } tmpl_cur_company.Restore();