TBTR: Mark vehicles as requiring service on template/replacement change

pull/507/head
Jonathan G Rennison 1 year ago
parent d30157a113
commit bd837a2fcd

@ -34,6 +34,7 @@
#include "table/train_cmd.h"
#include "tbtr_template_vehicle.h"
#include "tbtr_template_vehicle_func.h"
// since doing stuff with sprites
#include "newgrf_spritegroup.h"
@ -54,6 +55,7 @@ robin_hood::unordered_flat_map<GroupID, TemplateID> _template_replacement_index;
robin_hood::unordered_flat_map<GroupID, TemplateID> _template_replacement_index_recursive;
static void ReindexTemplateReplacementsRecursive();
static void MarkTrainsInGroupAsPendingTemplateReplacement(GroupID gid, const TemplateVehicle *tv);
void TemplateVehicleImageDimensions::SetFromTrain(const Train *t)
{
@ -147,6 +149,7 @@ TemplateReplacement::~TemplateReplacement()
_template_replacement_index.erase(this->Group());
ReindexTemplateReplacementsRecursive();
MarkTrainsInGroupAsPendingTemplateReplacement(this->Group(), nullptr);
}
void TemplateReplacement::PreCleanPool()
@ -155,6 +158,80 @@ void TemplateReplacement::PreCleanPool()
_template_replacement_index_recursive.clear();
}
bool ShouldServiceTrainForTemplateReplacement(const Train *t, const TemplateVehicle *tv)
{
const Company *c = Company::Get(t->owner);
if (tv->IsReplaceOldOnly() && !t->NeedsAutorenewing(c, false)) return false;
Money needed_money = c->settings.engine_renew_money;
if (needed_money > c->money) return false;
bool need_replacement = !TrainMatchesTemplate(t, tv);
if (need_replacement) {
/* Check money.
* We want 2*(the price of the whole template) without looking at the value of the vehicle(s) we are going to sell, or not need to buy. */
for (const TemplateVehicle *tv_unit = tv; tv_unit != nullptr; tv_unit = tv_unit->GetNextUnit()) {
if (!HasBit(Engine::Get(tv->engine_type)->company_avail, t->owner)) return false;
needed_money += 2 * Engine::Get(tv->engine_type)->GetCost();
}
return needed_money <= c->money;
} else if (!TrainMatchesTemplateRefit(t, tv) && tv->refit_as_template) {
return true;
} else {
return false;
}
}
static void MarkTrainsInGroupAsPendingTemplateReplacement(GroupID gid, const TemplateVehicle *tv)
{
std::vector<GroupID> groups;
groups.push_back(gid);
Owner owner = Group::Get(gid)->owner;
for (const Group *group : Group::Iterate()) {
if (group->vehicle_type != VEH_TRAIN || group->owner != owner || group->index == gid) continue;
auto is_descendant = [gid](const Group *g) -> bool {
while (true) {
if (g->parent == INVALID_GROUP) return false;
if (g->parent == gid) {
/* If this group has its own template defined, it's not a descendant for template inheriting purposes */
if (_template_replacement_index.find(g->index) != _template_replacement_index.end()) return false;
return true;
}
g = Group::Get(g->parent);
}
NOT_REACHED();
};
if (is_descendant(group)) {
groups.push_back(group->index);
}
}
std::sort(groups.begin(), groups.end());
for (Train *t : Train::Iterate()) {
if (!t->IsFrontEngine() || t->owner != owner || t->group_id >= NEW_GROUP) continue;
if (std::binary_search(groups.begin(), groups.end(), t->group_id)) {
SB(t->vehicle_flags, VF_REPLACEMENT_PENDING, 1, (tv != nullptr && ShouldServiceTrainForTemplateReplacement(t, tv)) ? 1 : 0);
}
}
}
void MarkTrainsUsingTemplateAsPendingTemplateReplacement(const TemplateVehicle *tv)
{
Owner owner = tv->owner;
for (Train *t : Train::Iterate()) {
if (!t->IsFrontEngine() || t->owner != owner || t->group_id >= NEW_GROUP) continue;
if (GetTemplateIDByGroupIDRecursive(t->group_id) == tv->index) {
SB(t->vehicle_flags, VF_REPLACEMENT_PENDING, 1, ShouldServiceTrainForTemplateReplacement(t, tv) ? 1 : 0);
}
}
}
TemplateReplacement *GetTemplateReplacementByGroupID(GroupID gid)
{
if (GetTemplateIDByGroupID(gid) == INVALID_TEMPLATE) return nullptr;
@ -190,11 +267,13 @@ bool IssueTemplateReplacement(GroupID gid, TemplateID tid)
tr->SetTemplate(tid);
_template_replacement_index[gid] = tid;
ReindexTemplateReplacementsRecursive();
MarkTrainsInGroupAsPendingTemplateReplacement(gid, TemplateVehicle::Get(tid));
return true;
} else if (TemplateReplacement::CanAllocateItem()) {
tr = new TemplateReplacement(gid, tid);
_template_replacement_index[gid] = tid;
ReindexTemplateReplacementsRecursive();
MarkTrainsInGroupAsPendingTemplateReplacement(gid, TemplateVehicle::Get(tid));
return true;
} else {
return false;

@ -224,6 +224,8 @@ TemplateReplacement *GetTemplateReplacementByGroupID(GroupID gid);
TemplateID GetTemplateIDByGroupID(GroupID gid);
TemplateID GetTemplateIDByGroupIDRecursive(GroupID gid);
bool IssueTemplateReplacement(GroupID gid, TemplateID tid);
bool ShouldServiceTrainForTemplateReplacement(const Train *t, const TemplateVehicle *tv);
void MarkTrainsUsingTemplateAsPendingTemplateReplacement(const TemplateVehicle *tv);
uint DeleteTemplateReplacementsByGroupID(GroupID g_id);

@ -2218,6 +2218,7 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u
TraceRestrictRemoveVehicleFromAllSlots(src->index);
ClrBit(src->vehicle_flags, VF_HAVE_SLOT);
}
ClrBit(src->vehicle_flags, VF_REPLACEMENT_PENDING);
OrderBackup::ClearVehicle(src);
}

@ -201,6 +201,7 @@ void VehicleServiceInDepot(Vehicle *v)
Ship::From(v)->critical_breakdown_count = 0;
}
v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
ClrBit(v->vehicle_flags, VF_REPLACEMENT_PENDING);
SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
do {
@ -231,6 +232,7 @@ bool Vehicle::NeedsServicing() const
if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
/* Are we ready for the next service cycle? */
bool needs_service = true;
const Company *c = Company::Get(this->owner);
if ((this->ServiceIntervalIsPercent() ?
(this->reliability >= this->GetEngine()->reliability * (100 - this->service_interval) / 100) :
@ -238,41 +240,29 @@ bool Vehicle::NeedsServicing() const
&& !(this->type == VEH_TRAIN && HasBit(Train::From(this)->flags, VRF_CONSIST_BREAKDOWN) && Train::From(this)->ConsistNeedsRepair())
&& !(this->type == VEH_ROAD && RoadVehicle::From(this)->critical_breakdown_count > 0)
&& !(this->type == VEH_SHIP && Ship::From(this)->critical_breakdown_count > 0)) {
needs_service = false;
}
if (!needs_service && !HasBit(this->vehicle_flags, VF_REPLACEMENT_PENDING)) {
return false;
}
/* If we're servicing anyway, because we have not disabled servicing when
* there are no breakdowns or we are playing with breakdowns, bail out. */
if (!_settings_game.order.no_servicing_if_no_breakdowns ||
_settings_game.difficulty.vehicle_breakdowns != 0) {
if (needs_service && (!_settings_game.order.no_servicing_if_no_breakdowns ||
_settings_game.difficulty.vehicle_breakdowns != 0)) {
return true;
}
/* Is vehicle old and renewing is enabled */
if (this->NeedsAutorenewing(c, true)) {
if (needs_service && this->NeedsAutorenewing(c, true)) {
return true;
}
if (this->type == VEH_TRAIN) {
TemplateVehicle *tv = GetTemplateVehicleByGroupIDRecursive(this->group_id);
const TemplateVehicle *tv = GetTemplateVehicleByGroupIDRecursive(this->group_id);
if (tv != nullptr) {
if (tv->IsReplaceOldOnly() && !this->NeedsAutorenewing(c, false)) return false;
Money needed_money = c->settings.engine_renew_money;
if (needed_money > c->money) return false;
bool need_replacement = !TrainMatchesTemplate(Train::From(this), tv);
if (need_replacement) {
/* Check money.
* We want 2*(the price of the whole template) without looking at the value of the vehicle(s) we are going to sell, or not need to buy. */
for (const TemplateVehicle *tv_unit = tv; tv_unit != nullptr; tv_unit = tv_unit->GetNextUnit()) {
if (!HasBit(Engine::Get(tv->engine_type)->company_avail, this->owner)) return false;
needed_money += 2 * Engine::Get(tv->engine_type)->GetCost();
}
return needed_money <= c->money;
} else if (!TrainMatchesTemplateRefit(Train::From(this), tv) && tv->refit_as_template) {
return true;
} else {
return false;
}
return ShouldServiceTrainForTemplateReplacement(Train::From(this), tv);
}
}
@ -4196,6 +4186,7 @@ void DumpVehicleFlagsGeneric(const Vehicle *v, T dump, U dump_header)
dump('a', "VF_AUTOMATE_TIMETABLE", HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE));
dump('Q', "VF_HAVE_SLOT", HasBit(v->vehicle_flags, VF_HAVE_SLOT));
dump('W', "VF_COND_ORDER_WAIT", HasBit(v->vehicle_flags, VF_COND_ORDER_WAIT));
dump('r', "VF_REPLACEMENT_PENDING", HasBit(v->vehicle_flags, VF_REPLACEMENT_PENDING));
dump_header("vcf:", "cached_veh_flags:");
dump('l', "VCF_LAST_VISUAL_EFFECT", HasBit(v->vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT));
dump('z', "VCF_GV_ZERO_SLOPE_RESIST", HasBit(v->vcache.cached_veh_flags, VCF_GV_ZERO_SLOPE_RESIST));

@ -63,6 +63,7 @@ enum VehicleFlags {
VF_AUTOMATE_TIMETABLE = 15, ///< Whether the vehicle should manage the timetable automatically.
VF_HAVE_SLOT = 16, ///< Vehicle has 1 or more slots
VF_COND_ORDER_WAIT = 17, ///< Vehicle is waiting due to conditional order loop
VF_REPLACEMENT_PENDING = 18, ///< Autoreplace or template replacement is pending, vehicle should visit the depot
};
/** Bit numbers used to indicate which of the #NewGRFCache values are valid. */

@ -947,6 +947,7 @@ CommandCost CmdToggleRefitAsTemplate(TileIndex tile, DoCommandFlag flags, uint32
if (should_execute) {
template_vehicle->ToggleRefitAsTemplate();
MarkTrainsUsingTemplateAsPendingTemplateReplacement(template_vehicle);
InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0);
}
@ -976,6 +977,7 @@ CommandCost CmdToggleTemplateReplaceOldOnly(TileIndex tile, DoCommandFlag flags,
if (should_execute) {
template_vehicle->ToggleReplaceOldOnly();
MarkTrainsUsingTemplateAsPendingTemplateReplacement(template_vehicle);
InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0);
}
@ -1307,7 +1309,12 @@ CommandCost CmdReplaceTemplateVehicle(TileIndex tile, DoCommandFlag flags, uint3
reindex = true;
}
}
if (reindex) ReindexTemplateReplacements();
if (reindex) {
ReindexTemplateReplacements();
MarkTrainsUsingTemplateAsPendingTemplateReplacement(template_vehicle);
}
} else if (template_vehicle->NumGroupsUsingTemplate() > 0) {
MarkTrainsUsingTemplateAsPendingTemplateReplacement(template_vehicle);
}
InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0);

Loading…
Cancel
Save