TBTR: Mark vehicles as requiring service on template/replacement change
This commit is contained in:
@@ -34,6 +34,7 @@
|
|||||||
#include "table/train_cmd.h"
|
#include "table/train_cmd.h"
|
||||||
|
|
||||||
#include "tbtr_template_vehicle.h"
|
#include "tbtr_template_vehicle.h"
|
||||||
|
#include "tbtr_template_vehicle_func.h"
|
||||||
|
|
||||||
// since doing stuff with sprites
|
// since doing stuff with sprites
|
||||||
#include "newgrf_spritegroup.h"
|
#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;
|
robin_hood::unordered_flat_map<GroupID, TemplateID> _template_replacement_index_recursive;
|
||||||
|
|
||||||
static void ReindexTemplateReplacementsRecursive();
|
static void ReindexTemplateReplacementsRecursive();
|
||||||
|
static void MarkTrainsInGroupAsPendingTemplateReplacement(GroupID gid, const TemplateVehicle *tv);
|
||||||
|
|
||||||
void TemplateVehicleImageDimensions::SetFromTrain(const Train *t)
|
void TemplateVehicleImageDimensions::SetFromTrain(const Train *t)
|
||||||
{
|
{
|
||||||
@@ -147,6 +149,7 @@ TemplateReplacement::~TemplateReplacement()
|
|||||||
|
|
||||||
_template_replacement_index.erase(this->Group());
|
_template_replacement_index.erase(this->Group());
|
||||||
ReindexTemplateReplacementsRecursive();
|
ReindexTemplateReplacementsRecursive();
|
||||||
|
MarkTrainsInGroupAsPendingTemplateReplacement(this->Group(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TemplateReplacement::PreCleanPool()
|
void TemplateReplacement::PreCleanPool()
|
||||||
@@ -155,6 +158,80 @@ void TemplateReplacement::PreCleanPool()
|
|||||||
_template_replacement_index_recursive.clear();
|
_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)
|
TemplateReplacement *GetTemplateReplacementByGroupID(GroupID gid)
|
||||||
{
|
{
|
||||||
if (GetTemplateIDByGroupID(gid) == INVALID_TEMPLATE) return nullptr;
|
if (GetTemplateIDByGroupID(gid) == INVALID_TEMPLATE) return nullptr;
|
||||||
@@ -190,11 +267,13 @@ bool IssueTemplateReplacement(GroupID gid, TemplateID tid)
|
|||||||
tr->SetTemplate(tid);
|
tr->SetTemplate(tid);
|
||||||
_template_replacement_index[gid] = tid;
|
_template_replacement_index[gid] = tid;
|
||||||
ReindexTemplateReplacementsRecursive();
|
ReindexTemplateReplacementsRecursive();
|
||||||
|
MarkTrainsInGroupAsPendingTemplateReplacement(gid, TemplateVehicle::Get(tid));
|
||||||
return true;
|
return true;
|
||||||
} else if (TemplateReplacement::CanAllocateItem()) {
|
} else if (TemplateReplacement::CanAllocateItem()) {
|
||||||
tr = new TemplateReplacement(gid, tid);
|
tr = new TemplateReplacement(gid, tid);
|
||||||
_template_replacement_index[gid] = tid;
|
_template_replacement_index[gid] = tid;
|
||||||
ReindexTemplateReplacementsRecursive();
|
ReindexTemplateReplacementsRecursive();
|
||||||
|
MarkTrainsInGroupAsPendingTemplateReplacement(gid, TemplateVehicle::Get(tid));
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@@ -224,6 +224,8 @@ TemplateReplacement *GetTemplateReplacementByGroupID(GroupID gid);
|
|||||||
TemplateID GetTemplateIDByGroupID(GroupID gid);
|
TemplateID GetTemplateIDByGroupID(GroupID gid);
|
||||||
TemplateID GetTemplateIDByGroupIDRecursive(GroupID gid);
|
TemplateID GetTemplateIDByGroupIDRecursive(GroupID gid);
|
||||||
bool IssueTemplateReplacement(GroupID gid, TemplateID tid);
|
bool IssueTemplateReplacement(GroupID gid, TemplateID tid);
|
||||||
|
bool ShouldServiceTrainForTemplateReplacement(const Train *t, const TemplateVehicle *tv);
|
||||||
|
void MarkTrainsUsingTemplateAsPendingTemplateReplacement(const TemplateVehicle *tv);
|
||||||
|
|
||||||
uint DeleteTemplateReplacementsByGroupID(GroupID g_id);
|
uint DeleteTemplateReplacementsByGroupID(GroupID g_id);
|
||||||
|
|
||||||
|
@@ -2218,6 +2218,7 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u
|
|||||||
TraceRestrictRemoveVehicleFromAllSlots(src->index);
|
TraceRestrictRemoveVehicleFromAllSlots(src->index);
|
||||||
ClrBit(src->vehicle_flags, VF_HAVE_SLOT);
|
ClrBit(src->vehicle_flags, VF_HAVE_SLOT);
|
||||||
}
|
}
|
||||||
|
ClrBit(src->vehicle_flags, VF_REPLACEMENT_PENDING);
|
||||||
OrderBackup::ClearVehicle(src);
|
OrderBackup::ClearVehicle(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -201,6 +201,7 @@ void VehicleServiceInDepot(Vehicle *v)
|
|||||||
Ship::From(v)->critical_breakdown_count = 0;
|
Ship::From(v)->critical_breakdown_count = 0;
|
||||||
}
|
}
|
||||||
v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
|
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
|
SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
|
||||||
|
|
||||||
do {
|
do {
|
||||||
@@ -231,6 +232,7 @@ bool Vehicle::NeedsServicing() const
|
|||||||
if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
|
if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
|
||||||
|
|
||||||
/* Are we ready for the next service cycle? */
|
/* Are we ready for the next service cycle? */
|
||||||
|
bool needs_service = true;
|
||||||
const Company *c = Company::Get(this->owner);
|
const Company *c = Company::Get(this->owner);
|
||||||
if ((this->ServiceIntervalIsPercent() ?
|
if ((this->ServiceIntervalIsPercent() ?
|
||||||
(this->reliability >= this->GetEngine()->reliability * (100 - this->service_interval) / 100) :
|
(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_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_ROAD && RoadVehicle::From(this)->critical_breakdown_count > 0)
|
||||||
&& !(this->type == VEH_SHIP && Ship::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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we're servicing anyway, because we have not disabled servicing when
|
/* If we're servicing anyway, because we have not disabled servicing when
|
||||||
* there are no breakdowns or we are playing with breakdowns, bail out. */
|
* there are no breakdowns or we are playing with breakdowns, bail out. */
|
||||||
if (!_settings_game.order.no_servicing_if_no_breakdowns ||
|
if (needs_service && (!_settings_game.order.no_servicing_if_no_breakdowns ||
|
||||||
_settings_game.difficulty.vehicle_breakdowns != 0) {
|
_settings_game.difficulty.vehicle_breakdowns != 0)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Is vehicle old and renewing is enabled */
|
/* Is vehicle old and renewing is enabled */
|
||||||
if (this->NeedsAutorenewing(c, true)) {
|
if (needs_service && this->NeedsAutorenewing(c, true)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->type == VEH_TRAIN) {
|
if (this->type == VEH_TRAIN) {
|
||||||
TemplateVehicle *tv = GetTemplateVehicleByGroupIDRecursive(this->group_id);
|
const TemplateVehicle *tv = GetTemplateVehicleByGroupIDRecursive(this->group_id);
|
||||||
if (tv != nullptr) {
|
if (tv != nullptr) {
|
||||||
if (tv->IsReplaceOldOnly() && !this->NeedsAutorenewing(c, false)) return false;
|
return ShouldServiceTrainForTemplateReplacement(Train::From(this), tv);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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('a', "VF_AUTOMATE_TIMETABLE", HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE));
|
||||||
dump('Q', "VF_HAVE_SLOT", HasBit(v->vehicle_flags, VF_HAVE_SLOT));
|
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('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_header("vcf:", "cached_veh_flags:");
|
||||||
dump('l', "VCF_LAST_VISUAL_EFFECT", HasBit(v->vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT));
|
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));
|
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_AUTOMATE_TIMETABLE = 15, ///< Whether the vehicle should manage the timetable automatically.
|
||||||
VF_HAVE_SLOT = 16, ///< Vehicle has 1 or more slots
|
VF_HAVE_SLOT = 16, ///< Vehicle has 1 or more slots
|
||||||
VF_COND_ORDER_WAIT = 17, ///< Vehicle is waiting due to conditional order loop
|
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. */
|
/** 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) {
|
if (should_execute) {
|
||||||
template_vehicle->ToggleRefitAsTemplate();
|
template_vehicle->ToggleRefitAsTemplate();
|
||||||
|
MarkTrainsUsingTemplateAsPendingTemplateReplacement(template_vehicle);
|
||||||
|
|
||||||
InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0);
|
InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0);
|
||||||
}
|
}
|
||||||
@@ -976,6 +977,7 @@ CommandCost CmdToggleTemplateReplaceOldOnly(TileIndex tile, DoCommandFlag flags,
|
|||||||
|
|
||||||
if (should_execute) {
|
if (should_execute) {
|
||||||
template_vehicle->ToggleReplaceOldOnly();
|
template_vehicle->ToggleReplaceOldOnly();
|
||||||
|
MarkTrainsUsingTemplateAsPendingTemplateReplacement(template_vehicle);
|
||||||
|
|
||||||
InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0);
|
InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0);
|
||||||
}
|
}
|
||||||
@@ -1307,7 +1309,12 @@ CommandCost CmdReplaceTemplateVehicle(TileIndex tile, DoCommandFlag flags, uint3
|
|||||||
reindex = true;
|
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);
|
InvalidateWindowClassesData(WC_TEMPLATEGUI_MAIN, 0);
|
||||||
|
Reference in New Issue
Block a user