From 757f67c1ba38452144fcc6e7bb358e1aa0457a76 Mon Sep 17 00:00:00 2001 From: FS-21 Date: Wed, 4 Oct 2023 17:11:09 +0200 Subject: [PATCH 1/3] Initial commit Customizable disguised target evaluation behaviour in new ScriptType attack actions. - By default, any unit with `DetectDisguise=yes` seeking targets via new [attack team missions introduced in Phobos](AI-Scripting-and-Mapping.md#10000-10049-attack-actions) always detects the disguised units. The probability to detect a disguised object now can be customized. - `DetectDisguise.Percent` values represent the probabilities in hard, medium & easy difficulty, in that order. In rulesmd.ini: [SOMETECHNO] ; TechnoType DetectDisguise.Percent=1.0, 1.0, 1.0 ; float, percents or absolute --- CREDITS.md | 1 + docs/Fixed-or-Improved-Logics.md | 11 +++++++++ docs/Whats-New.md | 1 + src/Ext/Script/Body.h | 2 +- src/Ext/Script/Mission.Attack.cpp | 41 +++++++++++++++++++++++++++++-- src/Ext/TechnoType/Body.cpp | 4 +++ src/Ext/TechnoType/Body.h | 3 +++ 7 files changed, 60 insertions(+), 3 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 86e41bb181..7e32a9d992 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -128,6 +128,7 @@ This page lists all the individual contributions to the project by their author. - Shared ammo logic - Customizable FLH when infantry is prone or deployed - Initial strength for cloned infantry + - Customizable disguised target evaluation behaviour in new ScriptType attack actions - **Starkku**: - Misc. minor bugfixes & improvements - AI script actions: diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 69c24d4456..8eaf56b162 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -463,6 +463,17 @@ In `rulesmd.ini`: TargetZoneScanType=same ; target zone scan enumeration (same|any|inrange) ``` +### Customizable disguised target evaluation behaviour in new ScriptType attack actions + +- By default, any unit with `DetectDisguise=yes` seeking targets via new [attack team missions introduced in Phobos](AI-Scripting-and-Mapping.md#10000-10049-attack-actions) always detects the disguised units. The probability to detect a disguised object now can be customized. +- `DetectDisguise.Percent` values represent the probabilities in hard, medium & easy difficulty, in that order. + +In `rulesmd.ini`: +```ini +[SOMETECHNO] ; TechnoType +DetectDisguise.Percent=1.0, 1.0, 1.0 ; float, percents or absolute +``` + ### Customizable unit image in art - `Image` tag in art INI is no longer limited to AnimationTypes and BuildingTypes, and can be applied to all TechnoTypes (InfantryTypes, VehicleTypes, AircraftTypes, BuildingTypes). diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 0ff67194a3..a6b5aa928e 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -348,6 +348,7 @@ New: - Customizable straight trajectory detonation & snap distance and pass-through option (by Starkku) - Airstrike & spy plane fixed spawn distance & height (by Starkku) - Allow enabling application of `Verses` and `PercentAtMax` for negative damage (by Starkku) +- Customizable disguised target evaluation behaviour in new ScriptType attack actions (by FS-21) Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) diff --git a/src/Ext/Script/Body.h b/src/Ext/Script/Body.h index 61ac3ce6bb..262ab550dd 100644 --- a/src/Ext/Script/Body.h +++ b/src/Ext/Script/Body.h @@ -219,7 +219,7 @@ class ScriptExt // Mission.Attack.cpp static void Mission_Attack(TeamClass* pTeam, bool repeatAction, int calcThreatMode, int attackAITargetType, int IdxAITargetTypeItem); - static TechnoClass* GreatestThreat(TechnoClass* pTechno, int method, int calcThreatMode, HouseClass* onlyTargetThisHouseEnemy, int attackAITargetType, int idxAITargetTypeItem, bool agentMode); + static TechnoClass* GreatestThreat(TechnoClass* pTechno, int method, int calcThreatMode, HouseClass* onlyTargetThisHouseEnemy, int attackAITargetType, int idxAITargetTypeItem, bool agentMode, std::vector disguiseDetection); static bool EvaluateObjectWithMask(TechnoClass* pTechno, int mask, int attackAITargetType, int idxAITargetTypeItem, TechnoClass* pTeamLeader); static void Mission_Attack_List(TeamClass* pTeam, bool repeatAction, int calcThreatMode, int attackAITargetType); static void Mission_Attack_List1Random(TeamClass* pTeam, bool repeatAction, int calcThreatMode, int attackAITargetType); diff --git a/src/Ext/Script/Mission.Attack.cpp b/src/Ext/Script/Mission.Attack.cpp index f3ddfd5ac0..a64d62e7f0 100644 --- a/src/Ext/Script/Mission.Attack.cpp +++ b/src/Ext/Script/Mission.Attack.cpp @@ -19,6 +19,7 @@ void ScriptExt::Mission_Attack(TeamClass* pTeam, bool repeatAction = true, int c bool agentMode = false; bool pacifistTeam = true; auto pTeamData = TeamExt::ExtMap.Find(pTeam); + std::vector disguiseDetection = {}; if (!pScript) return; @@ -136,6 +137,19 @@ void ScriptExt::Mission_Attack(TeamClass* pTeam, bool repeatAction = true, int c if ((pTypeInf->Agent && pTypeInf->Infiltrate) || pTypeInf->Engineer) agentMode = true; } + + auto pTechnoData = TechnoTypeExt::ExtMap.Find(pTechnoType); + if (pTechnoType->DetectDisguise) + { + auto const AIDifficulty = static_cast(pFoot->Owner->GetAIDifficultyIndex()); + double detectionValue = 1.0; + + if (pTechnoData->DetectDisguise_Percent.size() == 3) + detectionValue = pTechnoData->DetectDisguise_Percent[AIDifficulty]; + + detectionValue = detectionValue > 0.0 ? detectionValue : 1.0; + disguiseDetection.push_back(detectionValue); + } } } @@ -194,7 +208,7 @@ void ScriptExt::Mission_Attack(TeamClass* pTeam, bool repeatAction = true, int c enemyHouse = HouseClass::Array->GetItem(pLeaderUnit->Owner->EnemyHouseIndex); int targetMask = scriptArgument; - selectedTarget = GreatestThreat(pLeaderUnit, targetMask, calcThreatMode, enemyHouse, attackAITargetType, idxAITargetTypeItem, agentMode); + selectedTarget = GreatestThreat(pLeaderUnit, targetMask, calcThreatMode, enemyHouse, attackAITargetType, idxAITargetTypeItem, agentMode, disguiseDetection); if (selectedTarget) { @@ -399,7 +413,7 @@ void ScriptExt::Mission_Attack(TeamClass* pTeam, bool repeatAction = true, int c } } -TechnoClass* ScriptExt::GreatestThreat(TechnoClass* pTechno, int method, int calcThreatMode = 0, HouseClass* onlyTargetThisHouseEnemy = nullptr, int attackAITargetType = -1, int idxAITargetTypeItem = -1, bool agentMode = false) +TechnoClass* ScriptExt::GreatestThreat(TechnoClass* pTechno, int method, int calcThreatMode = 0, HouseClass* onlyTargetThisHouseEnemy = nullptr, int attackAITargetType = -1, int idxAITargetTypeItem = -1, bool agentMode = false, std::vector disguiseDetection = {}) { TechnoClass* bestObject = nullptr; double bestVal = -1; @@ -492,6 +506,29 @@ TechnoClass* ScriptExt::GreatestThreat(TechnoClass* pTechno, int method, int cal && object->Owner != pTechno->Owner && (!pTechno->Owner->IsAlliedWith(object) || IsUnitMindControlledFriendly(pTechno->Owner, object))) { + if (object->IsDisguised()) + { + if (disguiseDetection.size() == 0) + continue; + + bool detected = false; + + for (double item : disguiseDetection) + { + int dice = ScenarioClass::Instance->Random.RandomRanged(0, 99); + int detectionValue = std::round(item * 100.0); + + if (detectionValue > dice) + { + detected = true; + break; + } + } + + if (!detected) + continue; + } + double value = 0; if (EvaluateObjectWithMask(object, method, attackAITargetType, idxAITargetTypeItem, pTechno)) diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index e3c61db5ff..e210b67c18 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -273,6 +273,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->SpawnDistanceFromTarget.Read(exINI, pSection, "SpawnDistanceFromTarget"); this->SpawnHeight.Read(exINI, pSection, "SpawnHeight"); + this->DetectDisguise_Percent.Read(exINI, pSection, "DetectDisguise.Percent"); + // Ares 0.2 this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius"); @@ -569,6 +571,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->SpawnDistanceFromTarget) .Process(this->SpawnHeight) + + .Process(this->DetectDisguise_Percent) ; } void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index c3b21ea288..744b6a1276 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -182,6 +182,8 @@ class TechnoTypeExt Nullable SpawnDistanceFromTarget; Nullable SpawnHeight; + ValueableVector DetectDisguise_Percent; + struct LaserTrailDataEntry { ValueableIdx idxType; @@ -359,6 +361,7 @@ class TechnoTypeExt , SpawnDistanceFromTarget {} , SpawnHeight {} + , DetectDisguise_Percent {} { } virtual ~ExtData() = default; From 90a2e4d7ada73993a7bafe82a833dedb7bcf524d Mon Sep 17 00:00:00 2001 From: FS-21 Date: Mon, 24 Aug 2026 11:25:01 +0200 Subject: [PATCH 2/3] Address review feedback on DetectDisguise.Percent from PR #1169 --- src/Ext/Script/Body.h | 2 +- src/Ext/Script/Mission.Attack.cpp | 21 ++++++++++++++++----- src/Ext/TechnoType/Body.cpp | 19 +++++++++++++++++++ src/Ext/TechnoType/Body.h | 4 ++-- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/Ext/Script/Body.h b/src/Ext/Script/Body.h index 318bf999d2..02cc1a9c1b 100644 --- a/src/Ext/Script/Body.h +++ b/src/Ext/Script/Body.h @@ -234,7 +234,7 @@ class ScriptExt final : public AbstractExt // Mission.Attack.cpp static void Mission_Attack(TeamClass* pTeam, int calcThreatMode = 0, bool repeatAction = true, int attackAITargetType = -1, int idxAITargetTypeItem = -1); - static TechnoClass* GreatestThreat(TechnoClass* pTechno, int method, int calcThreatMode = 0, HouseClass* onlyTargetThisHouseEnemy = nullptr, int attackAITargetType = -1, int idxAITargetTypeItem = -1, bool agentMode = false, std::vector disguiseDetection = {}); + static TechnoClass* GreatestThreat(TechnoClass* pTechno, int method, int calcThreatMode = 0, HouseClass* onlyTargetThisHouseEnemy = nullptr, int attackAITargetType = -1, int idxAITargetTypeItem = -1, bool agentMode = false, const std::vector& disguiseDetection = {}); static bool EvaluateObjectWithMask(TechnoClass* pTechno, int mask, int attackAITargetType = -1, int idxAITargetTypeItem = -1, TechnoClass* pTeamLeader = nullptr); static void Mission_Attack_List(TeamClass* pTeam, int calcThreatMode = 0, bool repeatAction = true, int attackAITargetType = -1); static void Mission_Attack_List1Random(TeamClass* pTeam, int calcThreatMode = 0, bool repeatAction = true, int attackAITargetType = -1); diff --git a/src/Ext/Script/Mission.Attack.cpp b/src/Ext/Script/Mission.Attack.cpp index 6e3f384681..54ede5ecca 100644 --- a/src/Ext/Script/Mission.Attack.cpp +++ b/src/Ext/Script/Mission.Attack.cpp @@ -144,13 +144,24 @@ void ScriptExt::Mission_Attack(TeamClass* pTeam, int calcThreatMode, bool repeat auto pTechnoData = TechnoTypeExt::Fetch(pTechnoType); if (pTechnoType->DetectDisguise) { - auto const AIDifficulty = static_cast(pFoot->Owner->GetAIDifficultyIndex()); + auto const AIDifficulty = pFoot->Owner->GetAIDifficultyIndex(); + const auto& percent = pTechnoData->DetectDisguise_Percent.Get(); double detectionValue = 1.0; - if (pTechnoData->DetectDisguise_Percent.size() == 3) - detectionValue = pTechnoData->DetectDisguise_Percent[AIDifficulty]; + switch (AIDifficulty) + { + case 0: + detectionValue = percent.X; + break; + case 1: + detectionValue = percent.Y; + break; + case 2: + default: + detectionValue = percent.Z; + break; + } - detectionValue = detectionValue > 0.0 ? detectionValue : 1.0; disguiseDetection.push_back(detectionValue); } } @@ -465,7 +476,7 @@ void ScriptExt::Mission_Attack(TeamClass* pTeam, int calcThreatMode, bool repeat } } -TechnoClass* ScriptExt::GreatestThreat(TechnoClass* pTechno, int method, int calcThreatMode, HouseClass* onlyTargetThisHouseEnemy, int attackAITargetType, int idxAITargetTypeItem, bool agentMode, std::vector disguiseDetection) +TechnoClass* ScriptExt::GreatestThreat(TechnoClass* pTechno, int method, int calcThreatMode, HouseClass* onlyTargetThisHouseEnemy, int attackAITargetType, int idxAITargetTypeItem, bool agentMode, const std::vector& disguiseDetection) { TechnoClass* pBestObject = nullptr; double bestVal = -1; diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index cb24e4baff..fddfd14015 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -959,7 +959,26 @@ void TechnoTypeExt::LoadFromINIFile(CCINIClass* const pINI) this->DriverKilled_KillPassengers.Read(exINI, pSection, "DriverKilled.KillPassengers"); this->DeployFireWeapon.Read(exINI, pSection, "DeployFireWeapon"); this->TargetZoneScanType.Read(exINI, pSection, "TargetZoneScanType"); + this->DetectDisguise_Percent.Read(exINI, pSection, "DetectDisguise.Percent"); + { + auto& percent = *this->DetectDisguise_Percent.GetEx(); + if (percent.ValueCount == 1) + { + percent.Y = percent.X; + percent.Z = percent.X; + percent.ValueCount = 3; + } + else if (percent.ValueCount == 2) + { + percent.Z = percent.Y; + percent.ValueCount = 3; + } + + percent.X = std::clamp(percent.X, 0.0, 1.0); + percent.Y = std::clamp(percent.Y, 0.0, 1.0); + percent.Z = std::clamp(percent.Z, 0.0, 1.0); + } this->AreaGuardRange.Read(exINI, pSection, "AreaGuardRange"); this->MaxGuardRange.Read(exINI, pSection, "MaxGuardRange"); diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 62ef9a7974..6c1775967a 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -297,7 +297,7 @@ class TechnoTypeExt : public ObjectTypeExt Valueable CrashSpin_Multiplier; - ValueableVector DetectDisguise_Percent; + Valueable> DetectDisguise_Percent; Nullable AINormalTargetingDelay; Nullable PlayerNormalTargetingDelay; @@ -711,7 +711,7 @@ class TechnoTypeExt : public ObjectTypeExt , MakesWake { } , CrashSpin_Multiplier { 1.0f } - , DetectDisguise_Percent {} + , DetectDisguise_Percent { { 1.0, 1.0, 1.0 } } , AINormalTargetingDelay {} , PlayerNormalTargetingDelay {} From 617fa33256fbfe3bd7cf28a420cc288c05ef2ac6 Mon Sep 17 00:00:00 2001 From: FS-21 Date: Fri, 28 Aug 2026 09:16:26 +0200 Subject: [PATCH 3/3] fixes --- src/Ext/TechnoType/Body.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 0d3b2ab346..84440419cf 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1156,8 +1156,6 @@ void TechnoTypeExt::LoadFromINIFile(CCINIClass* const pINI) this->FallingDownDamage_Water.Read(exINI, pSection, "FallingDownDamage.Water"); this->FallingDownDamage_AllowEMP.Read(exINI, pSection, "FallingDownDamage.AllowEMP"); - this->DetectDisguise_Percent.Read(exINI, pSection, "DetectDisguise.Percent"); - this->EngineerRepairAmount.Read(exINI, pSection, "EngineerRepairAmount"); this->DebrisTypes_Limit.Read(exINI, pSection, "DebrisTypes.Limit");