diff --git a/CREDITS.md b/CREDITS.md index 6a2035cd83..69b8ab99fd 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -465,6 +465,7 @@ This page lists all the individual contributions to the project by their author. - Customize if cloning need power - Customize type selection for IFV - Fix the issue that units will goto farest location if target is closer than `MinimumRange` + - Allow techno type considered as other type when AI recruit techno for teams - **Apollo** - Translucent SHP drawing patches - **ststl**: - Customizable `ShowTimer` priority of superweapons diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index c7cfb17940..040f854c9b 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -1820,6 +1820,16 @@ HeightShadowScaling.MinScale=0.0 ; floating point value ShadowSizeCharacteristicHeight= ; integer, height in leptons ``` +### Allow techno type considered as other type when AI recruit techno for teams + +- It is now possible to make techno type considered as other type when AI recruit techno for teams. + +In `rulesmd.ini`: +```ini +[SOMETECHNO] ; TechnoType +TeamMember.ConsideredAs= ; list of technotypes +``` + ## Terrains ### Animated TerrainTypes diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 6ad7bee8f6..9be4474f51 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -466,6 +466,7 @@ New: - [Customize type selection for IFV](Fixed-or-Improved-Logics.md#customize-type-selection-for-ifv) (by NetsuNegi) - CellSpread in cylinder shape (by TaranDahl) - CellSpread damage check if victim is in air or on floor (by TaranDahl) +- Allow techno type considered as other type when AI recruit techno for teams (by NetsuNegi) Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) diff --git a/src/Ext/Team/Hooks.cpp b/src/Ext/Team/Hooks.cpp index 64688d2f17..f3f7b1abb8 100644 --- a/src/Ext/Team/Hooks.cpp +++ b/src/Ext/Team/Hooks.cpp @@ -1,5 +1,6 @@ #include "Body.h" #include +#include // Bugfix: TAction 7,80,107. DEFINE_HOOK(0x65DF67, TeamTypeClass_CreateMembers_LoadOntoTransport, 0x6) @@ -53,3 +54,95 @@ DEFINE_HOOK(0x65DF67, TeamTypeClass_CreateMembers_LoadOntoTransport, 0x6) return 0x65DF8D; } + +DEFINE_HOOK(0x6EA6BE, TeamClass_CanAddMember_Consideration, 0x6) +{ + enum { SkipGameCode = 0x6EA6F2 }; + + GET(TeamClass*, pTeam, EBP); + GET(FootClass*, pFoot, ESI); + GET(int*, idx, EBX); + const auto pFootType = pFoot->GetTechnoType(); + const auto pFootTypeExt = TechnoTypeExt::ExtMap.Find(pFootType); + const auto pTaskForce = pTeam->Type->TaskForce; + + do + { + const auto pType = pTaskForce->Entries[*idx].Type; + + if (pType == pFootType || pFootTypeExt->TeamMember_ConsideredAs.Contains(pType)) + break; + + *idx = *idx + 1; + } + while (pTaskForce->CountEntries > *idx); + + return SkipGameCode; +} + +DEFINE_HOOK(0x6EA8E7, TeamClass_LiberateMember_Consideration, 0x5) +{ + enum { SkipGameCode = 0x6EA91B }; + + GET(TeamClass*, pTeam, EDI); + GET(FootClass*, pMember, EBP); + int idx = 0; + const auto pMemberType = pMember->GetTechnoType(); + const auto pMemberTypeExt = TechnoTypeExt::ExtMap.Find(pMemberType); + const auto pTaskForce = pTeam->Type->TaskForce; + + do + { + const auto pSearchType = pTaskForce->Entries[idx].Type; + + if (pSearchType == pMemberType || pMemberTypeExt->TeamMember_ConsideredAs.Contains(pSearchType)) + break; + + ++idx; + } + while (pTaskForce->CountEntries > idx); + + R->Stack(STACK_OFFSET(0x14, 0x8), idx); + return SkipGameCode; +} + +DEFINE_HOOK(0x6EAD73, TeamClass_RecruitMember_Consideration, 0x7) +{ + enum { ContinueCheck = 0x6EAD8F, SkipThisMember = 0x6EADB3 }; + + GET(TeamClass*, pTeam, ECX); + GET(UnitClass*, pMember, ESI); + GET_STACK(int, idx, STACK_OFFSET(0x3C, 0x4)); + const auto pMemberType = pMember->Type; + const auto pTaskForce = pTeam->Type->TaskForce; + const auto pSearchType = pTaskForce->Entries[idx].Type; + + return pSearchType == pMemberType || TechnoTypeExt::ExtMap.Find(pMemberType)->TeamMember_ConsideredAs.Contains(pSearchType) ? ContinueCheck : SkipThisMember; +} + +DEFINE_HOOK(0x6EF57F, TeamClass_GetTaskForceMissingMemberTypes_Consideration, 0x5) +{ + enum { ContinueIn = 0x6EF584, SkipThisMember = 0x6EF5A5 }; + + GET(int, idx, EAX); + + if (idx != -1) + return ContinueIn; + + GET(DynamicVectorClass*, vector, ESI); + GET(FootClass*, pMember, EDI); + const auto pMemberTypeExt = TechnoTypeExt::ExtMap.Find(pMember->GetTechnoType()); + + for (const auto pConsideType : pMemberTypeExt->TeamMember_ConsideredAs) + { + idx = vector->FindItemIndex(pConsideType); + + if (idx != -1) + { + R->EAX(idx); + return ContinueIn; + } + } + + return SkipThisMember; +} diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index faa10a6775..c55da72f9e 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1035,6 +1035,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) Debug::Log("[Developer warning][%s] Ammo.AutoConvertMinimumAmount is greater than Ammo.AutoConvertMaximumAmount, resulting in no conversion.\n", pSection); this->InfantryAutoDeploy.Read(exINI, pSection, "InfantryAutoDeploy"); + + this->TeamMember_ConsideredAs.Read(exINI, pSection, "TeamMember.ConsideredAs"); // Ares 0.2 this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius"); @@ -1697,6 +1699,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->InfantryAutoDeploy) + .Process(this->TeamMember_ConsideredAs) + .Process(this->TurretResponse) ; } diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 35c5ac4966..9867b6f42d 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -443,6 +443,8 @@ class TechnoTypeExt Nullable InfantryAutoDeploy; + ValueableVector TeamMember_ConsideredAs; + Nullable TurretResponse; ExtData(TechnoTypeClass* OwnerObject) : Extension(OwnerObject) @@ -839,6 +841,8 @@ class TechnoTypeExt , InfantryAutoDeploy {} + , TeamMember_ConsideredAs {} + , TurretResponse {} { }