diff --git a/CREDITS.md b/CREDITS.md index f3b359298d..df7d63d42e 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -766,6 +766,7 @@ This page lists all the individual contributions to the project by their author. - Country-specific veteran buildings - Fix the Spotlight-transport interaction bug caused by the incorrect reference removal fix - Fix the issue that *Customizable crew type per country* not considering parsing order caused game parsing failure and a warning in the log + - Customize `IdleActionFrequency` - **Ollerus**: - Build limit group enhancement - Customizable rocker amplitude diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 6452652d71..1bcd4cff12 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -2748,6 +2748,18 @@ In `rulesmd.ini`: HarvesterLoadRate= ; integer, default to [General] -> HarvesterLoadRate ``` +### Customize `IdleActionFrequency` + +- Now `IdleActionFrequency` can be customized on each infantry. + - With a single value, the interval is a random value between `IdleActionFrequency * 450` and `IdleActionFrequency * 1800` frames, which customizes the frequency the same way the global value does. + - With two values, the first one directly sets the lower bound and the second one the upper bound of the random interval in frames. + +In `rulesmd.ini`: +```ini +[SOMEINFANTRY] ; InfantryType +IdleActionFrequency= ; a single floating point value, or a pair of integers defining the random delay range in frames (min, max), defaults to [AudioVisual] -> IdleActionFrequency +``` + ### Customize type selection for IFV - In vanilla game, when using type selection command on IFVs, all of them will be selected regardless of their current modes, which is allowed to customize now. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 2cf117e54f..26f8dad015 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -444,6 +444,7 @@ HideShakeEffects=false ; boolean - Country-based attached effects (by Ollerus) - [Customizable crew type per country](Fixed-or-Improved-Logics.md#customizable-crew-type-per-country) (by Sovietianqi, FlyStar, Noble_Fish) - [Country-specific veteran buildings](Fixed-or-Improved-Logics.md#country-specific-veteran-buildings) (by Noble_Fish) +- [Customize `IdleActionFrequency`](Fixed-or-Improved-Logics.md#customize-idleactionfrequency) (by Noble_Fish) #### Vanilla fixes: - Fixed the bug where a building with `Factory=BuildingType` owned by the AI did not play `ProductionAnim` when placing a produced building (by Noble_Fish) diff --git a/src/Ext/Infantry/Hooks.cpp b/src/Ext/Infantry/Hooks.cpp index 1f20e685fd..1fadf04dda 100644 --- a/src/Ext/Infantry/Hooks.cpp +++ b/src/Ext/Infantry/Hooks.cpp @@ -275,3 +275,34 @@ DEFINE_HOOK(0x51DA44, InfantryClass_DoAction_SequenceRate_Store, 0x6) } #pragma endregion + +DEFINE_HOOK_AGAIN(0x51CDD9, InfantryClass_UpdateIdleAction_IdleActionFrequency, 0x6) +DEFINE_HOOK(0x51CDEF, InfantryClass_UpdateIdleAction_IdleActionFrequency, 0x6) +{ + GET(InfantryClass* const, pThis, ESI); + auto const pTypeExt = InfantryTypeExt::Fetch(pThis->Type); + + if (auto const pRange = pTypeExt->IdleActionFrequency.GetEx()) + { + const bool isUpperBound = R->Origin() == 0x51CDD9; + double value; + + if (pRange->ValueCount >= 2) + { + // Two values set the delay range directly in frames, + const double lower = std::min(pRange->X, pRange->Y); + const double upper = std::max(pRange->X, pRange->Y); + value = isUpperBound ? upper / 1800.0 : lower / 450.0; + } + else + { + value = pRange->X; + } + + __asm { fld value } + + return R->Origin() + 0x6; + } + + return 0; +} diff --git a/src/Ext/InfantryType/Body.cpp b/src/Ext/InfantryType/Body.cpp index 83a25474cf..384c54d999 100644 --- a/src/Ext/InfantryType/Body.cpp +++ b/src/Ext/InfantryType/Body.cpp @@ -53,6 +53,7 @@ void InfantryTypeExt::LoadFromINIFile(CCINIClass* const pINI) if (exArtINI.ReadBool(pRateSection, key, &normalized)) this->CustomSequenceNormalized[i] = normalized ? 1 : 0; } + this->IdleActionFrequency.Read(exINI, pSection, "IdleActionFrequency"); } template @@ -77,6 +78,7 @@ void InfantryTypeExt::Serialize(T& Stm) .Process(this->InfantryAutoDeploy) .Process(this->CustomSequenceRates) .Process(this->CustomSequenceNormalized) + .Process(this->IdleActionFrequency) ; } diff --git a/src/Ext/InfantryType/Body.h b/src/Ext/InfantryType/Body.h index e2ebeb14b7..2d32ec8120 100644 --- a/src/Ext/InfantryType/Body.h +++ b/src/Ext/InfantryType/Body.h @@ -27,6 +27,7 @@ class InfantryTypeExt final : public TechnoTypeExt std::vector> DeployedWeaponBurstFLHs; std::vector> EliteDeployedWeaponBurstFLHs; Nullable InfantryAutoDeploy; + Nullable> IdleActionFrequency; // Per-sequence animation rates read from the infantry's art section std::vector CustomSequenceRates; @@ -49,6 +50,7 @@ class InfantryTypeExt final : public TechnoTypeExt , InfantryAutoDeploy {} , CustomSequenceRates(42, -1) , CustomSequenceNormalized(42, -1) + , IdleActionFrequency {} { } InfantryTypeClass* OwnerObject() const