diff --git a/src/engine.cpp b/src/engine.cpp index 8fe28f51b2..66b7ba77c0 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -207,7 +207,19 @@ uint Engine::DetermineCapacity(const Vehicle *v, uint16 *mail_capacity) const /* Check the refit capacity callback if we are not in the default configuration, or if we are using the new multiplier algorithm. */ if (HasBit(this->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) && (new_multipliers || default_cargo != cargo_type || (v != nullptr && v->cargo_subtype != 0))) { - uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, this->index, v); + uint16 callback; + if (this->refit_capacity_values != nullptr) { + const EngineRefitCapacityValue *caps = this->refit_capacity_values.get(); + while (true) { + if (HasBit(caps->cargoes, cargo_type)) { + callback = caps->capacity; + break; + } + caps++; + } + } else { + callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, this->index, v); + } if (callback != CALLBACK_FAILED) return callback; } diff --git a/src/engine_base.h b/src/engine_base.h index 39899f302a..c2911b4863 100644 --- a/src/engine_base.h +++ b/src/engine_base.h @@ -27,6 +27,11 @@ struct WagonOverride { typedef Pool EnginePool; extern EnginePool _engine_pool; +struct EngineRefitCapacityValue { + CargoTypes cargoes; + uint32 capacity; +}; + struct Engine : EnginePool::PoolItem<&_engine_pool> { TinyString name; ///< Custom name of engine. Date intro_date; ///< Date of introduction of the engine. @@ -72,6 +77,8 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> { uint64 cb36_properties_used = UINT64_MAX; btree::btree_map sprite_group_cb36_properties_used; + std::unique_ptr refit_capacity_values; + Engine() {} Engine(VehicleType type, EngineID base); bool IsEnabled() const; diff --git a/src/linkgraph/refresh.cpp b/src/linkgraph/refresh.cpp index 3f7155dd66..2ccc402f74 100644 --- a/src/linkgraph/refresh.cpp +++ b/src/linkgraph/refresh.cpp @@ -111,7 +111,10 @@ bool LinkRefresher::HandleRefit(CargoID refit_cargo) CargoID temp_cid = v->cargo_type; byte temp_subtype = v->cargo_subtype; v->cargo_type = this->cargo; - v->cargo_subtype = GetBestFittingSubType(v, v, this->cargo); + if (e->refit_capacity_values == nullptr || !(e->callbacks_used & SGCU_REFIT_CB_ALL_CARGOES) || this->cargo == e->GetDefaultCargoType() || (e->type == VEH_AIRCRAFT && IsCargoInClass(this->cargo, CC_PASSENGERS))) { + /* This can be omitted when the refit capacity values are already determined, and the capacity is definitely from the refit callback */ + v->cargo_subtype = GetBestFittingSubType(v, v, this->cargo); + } uint16 mail_capacity = 0; uint amount = e->DetermineCapacity(v, &mail_capacity); diff --git a/src/newgrf_commons.h b/src/newgrf_commons.h index 473123da73..399cba098a 100644 --- a/src/newgrf_commons.h +++ b/src/newgrf_commons.h @@ -335,11 +335,12 @@ struct GRFFileProps : GRFFilePropsBase<1> { enum SpriteGroupCallbacksUsed : uint8 { SGCU_NONE = 0, - SGCU_ALL = 0xFF, + SGCU_ALL = 0xF, SGCU_VEHICLE_32DAY_CALLBACK = 1 << 0, SGCU_VEHICLE_REFIT_COST = 1 << 1, SGCU_RANDOM_TRIGGER = 1 << 2, SGCU_CB36_SPEED_RAILTYPE = 1 << 3, + SGCU_REFIT_CB_ALL_CARGOES = 1 << 4, }; DECLARE_ENUM_AS_BIT_SET(SpriteGroupCallbacksUsed) diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index 746748d393..1aaa93535c 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -1587,13 +1587,18 @@ void FillNewGRFVehicleCache(const Vehicle *v) void AnalyseEngineCallbacks() { btree::btree_map sg_cb36; + btree::btree_map cb_refit_cap_values; for (Engine *e : Engine::Iterate()) { sg_cb36.clear(); e->sprite_group_cb36_properties_used.clear(); + e->refit_capacity_values.reset(); SpriteGroupCallbacksUsed callbacks_used = SGCU_NONE; uint64 cb36_properties_used = 0; - auto process_sg = [&](const SpriteGroup *sg) { + bool refit_cap_whitelist_ok = true; + bool refit_cap_no_var_47 = true; + uint non_purchase_groups = 0; + auto process_sg = [&](const SpriteGroup *sg, bool is_purchase) { if (sg == nullptr) return; AnalyseCallbackOperation op; @@ -1601,13 +1606,16 @@ void AnalyseEngineCallbacks() callbacks_used |= op.callbacks_used; cb36_properties_used |= op.properties_used; sg_cb36[sg] = op.properties_used; + if ((op.result_flags & ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND) && !is_purchase) refit_cap_whitelist_ok = false; + if ((op.result_flags & ACORF_CB_REFIT_CAP_SEEN_VAR_47) && !is_purchase) refit_cap_no_var_47 = false; + if (!is_purchase) non_purchase_groups++; }; for (uint i = 0; i < NUM_CARGO + 2; i++) { - process_sg(e->grf_prop.spritegroup[i]); + process_sg(e->grf_prop.spritegroup[i], i == CT_PURCHASE); } for (const WagonOverride &wo : e->overrides) { - process_sg(wo.group); + process_sg(wo.group, false); } e->callbacks_used = callbacks_used; e->cb36_properties_used = cb36_properties_used; @@ -1616,6 +1624,33 @@ void AnalyseEngineCallbacks() e->sprite_group_cb36_properties_used[iter.first] = iter.second; } } + + if (refit_cap_whitelist_ok && non_purchase_groups <= 1 && HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) && e->grf_prop.spritegroup[CT_DEFAULT] != nullptr) { + const SpriteGroup *purchase_sg = e->grf_prop.spritegroup[CT_PURCHASE]; + e->grf_prop.spritegroup[CT_PURCHASE] = nullptr; // Temporarily disable separate purchase sprite group + if (refit_cap_no_var_47) { + cb_refit_cap_values[GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, e->index, nullptr)] = ALL_CARGOTYPES; + } else { + const CargoID default_cb = e->info.cargo_type; + for (CargoID c = 0; c < NUM_CARGO; c++) { + e->info.cargo_type = c; + cb_refit_cap_values[GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, e->index, nullptr)] |= (static_cast(1) << c); + } + e->info.cargo_type = default_cb; + } + e->grf_prop.spritegroup[CT_PURCHASE] = purchase_sg; + bool all_ok = true; + uint index = 0; + e->refit_capacity_values.reset(MallocT(cb_refit_cap_values.size())); + for (const auto &iter : cb_refit_cap_values) { + if (iter.first == CALLBACK_FAILED) all_ok = false; + e->refit_capacity_values.get()[index] = { iter.second, iter.first }; + index++; + } + if (all_ok) e->callbacks_used |= SGCU_REFIT_CB_ALL_CARGOES; + + cb_refit_cap_values.clear(); + } } } diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index 13480f3120..93e576b346 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -343,6 +343,10 @@ void DeterministicSpriteGroup::AnalyseCallbacks(AnalyseCallbackOperation &op) co if (check_1A_range()) return; + if ((op.mode == ACOM_CB_VAR || op.mode == ACOM_CB_REFIT_CAPACITY) && this->var_scope != VSG_SCOPE_SELF) { + op.result_flags |= ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND; + } + auto find_cb_result = [&](const SpriteGroup *group, AnalyseCallbackOperation::FindCBResultData data) -> bool { if (group == nullptr) return false; AnalyseCallbackOperation cbr_op; @@ -356,6 +360,7 @@ void DeterministicSpriteGroup::AnalyseCallbacks(AnalyseCallbackOperation &op) co const auto &adjust = this->adjusts[0]; if (op.mode == ACOM_CB_VAR && adjust.variable == 0xC) { if (adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) { + bool found_refit_cap = false; for (const auto &range : this->ranges) { if (range.low == range.high) { switch (range.low) { @@ -380,12 +385,30 @@ void DeterministicSpriteGroup::AnalyseCallbacks(AnalyseCallbackOperation &op) co op.callbacks_used |= cb36_op.callbacks_used; } break; + + case CBID_VEHICLE_REFIT_CAPACITY: + found_refit_cap = true; + if (range.group != nullptr) { + AnalyseCallbackOperation cb_refit_op; + cb_refit_op.mode = ACOM_CB_REFIT_CAPACITY; + range.group->AnalyseCallbacks(cb_refit_op); + op.result_flags |= (cb_refit_op.result_flags & (ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND | ACORF_CB_REFIT_CAP_SEEN_VAR_47)); + } + break; } } else { if (range.group != nullptr) range.group->AnalyseCallbacks(op); } } - if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); + if (this->default_group != nullptr) { + AnalyseCallbackOperationResultFlags prev_result = op.result_flags; + this->default_group->AnalyseCallbacks(op); + if (found_refit_cap) { + const AnalyseCallbackOperationResultFlags save_mask = ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND | ACORF_CB_REFIT_CAP_SEEN_VAR_47; + op.result_flags &= ~save_mask; + op.result_flags |= (prev_result & save_mask); + } + } return; } } @@ -516,6 +539,12 @@ void DeterministicSpriteGroup::AnalyseCallbacks(AnalyseCallbackOperation &op) co op.properties_used |= UINT64_MAX; } } + if ((op.mode == ACOM_CB_VAR || op.mode == ACOM_CB_REFIT_CAPACITY) && !(adjust.variable == 0xC || adjust.variable == 0x1A || adjust.variable == 0x47 || adjust.variable == 0x7D || adjust.variable == 0x7E)) { + op.result_flags |= ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND; + } + if ((op.mode == ACOM_CB_VAR || op.mode == ACOM_CB_REFIT_CAPACITY) && adjust.variable == 0x47) { + op.result_flags |= ACORF_CB_REFIT_CAP_SEEN_VAR_47; + } if (adjust.variable == 0x7E && adjust.subroutine != nullptr) { adjust.subroutine->AnalyseCallbacks(op); } @@ -571,6 +600,8 @@ const SpriteGroup *RandomizedSpriteGroup::Resolve(ResolverObject &object) const void RandomizedSpriteGroup::AnalyseCallbacks(AnalyseCallbackOperation &op) const { + op.result_flags |= ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND; + if (op.mode == ACOM_CB_VAR) op.callbacks_used |= SGCU_RANDOM_TRIGGER; for (const SpriteGroup *group: this->groups) { diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index dbd7ff59b1..6cb9bbb9bd 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -55,6 +55,7 @@ enum AnalyseCallbackOperationMode { ACOM_FIND_CB_RESULT, ACOM_CB36_SPEED, ACOM_INDUSTRY_TILE, + ACOM_CB_REFIT_CAPACITY, }; struct AnalyseCallbackOperationIndustryTileData; @@ -62,6 +63,8 @@ struct AnalyseCallbackOperationIndustryTileData; enum AnalyseCallbackOperationResultFlags { ACORF_NONE = 0, ACORF_CB_RESULT_FOUND = 1 << 0, + ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND = 1 << 1, + ACORF_CB_REFIT_CAP_SEEN_VAR_47 = 1 << 2, }; DECLARE_ENUM_AS_BIT_SET(AnalyseCallbackOperationResultFlags) diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index 6c3ff1245e..028d078ec9 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -373,6 +373,16 @@ class NIHVehicle : public NIHelper { output.print(buffer); } } + if (e->refit_capacity_values != nullptr) { + const EngineRefitCapacityValue *caps = e->refit_capacity_values.get(); + CargoTypes seen = 0; + while (seen != ALL_CARGOTYPES) { + seprintf(buffer, lastof(buffer), " Refit capacity cache: cargoes: 0x" OTTD_PRINTFHEX64 " --> 0x%X", caps->cargoes, caps->capacity); + output.print(buffer); + seen |= caps->cargoes; + caps++; + } + } YearMonthDay ymd; ConvertDateToYMD(e->intro_date, &ymd); seprintf(buffer, lastof(buffer), " Intro: %4i-%02i-%02i, Age: %u, Base life: %u, Durations: %u %u %u (sum: %u)",