From c6862df879b394bfeae5015cecfa7400a1cac5aa Mon Sep 17 00:00:00 2001 From: FS-21 Date: Wed, 8 Oct 2025 14:02:16 +0200 Subject: [PATCH 1/7] Initial commit Some vanilla script actions now have a Phobos equivalent that you have to write the object's ID instead of index. String reading support in ScriptType action arguments. The script must support this. --- CREDITS.md | 1 + docs/AI-Scripting-and-Mapping.md | 126 +++++++++++++++++++++++++- docs/Whats-New.md | 1 + src/Ext/Script/Body.h | 15 +++- src/Ext/Script/Hooks.cpp | 150 +++++++++++++++++++++++++++++++ 5 files changed, 291 insertions(+), 2 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 6dbdbefa02..ae653d1900 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -152,6 +152,7 @@ This page lists all the individual contributions to the project by their author. - Warhead activation target health thresholds enhancements - Event 606: AttachEffect is attaching to a Techno - Linked superweapons + - New ScriptTypeActions `19017, 19018, 19020, 19024, 19025, 19026, 19027, 19046, 19047, 19051, 19056, 19058` that require ID as argument - **Starkku**: - Misc. minor bugfixes & improvements - AI script actions: diff --git a/docs/AI-Scripting-and-Mapping.md b/docs/AI-Scripting-and-Mapping.md index e7a6f81cc5..51f2586ba6 100644 --- a/docs/AI-Scripting-and-Mapping.md +++ b/docs/AI-Scripting-and-Mapping.md @@ -492,7 +492,131 @@ x=i,n ; where 18048 <= i <= 18071, n is made up of two parts, the lo ### `19000-19999` Miscellanous/Uncategorized -This category is empty for now. +#### `19017` Change Script + +- Similar to action 17, but uses a string ID from the `[ScriptTypes]` list instead of an index. + +In `aimd.ini`: +```ini +[SOMESCRIPTTYPE] ; ScriptType +x=19017,ID ; text +``` + +#### `19018` Change TeamType + +- Similar to action 18, but uses a string ID from the `[TeamTypes]` list instead of an index. + +In `aimd.ini`: +```ini +[SOMESCRIPTTYPE] ; ScriptType +x=19018,ID ; text +``` + +#### `19020` Change House + +- Similar to action 20, but uses a string ID from the `[Houses]` list instead of an index. + +In `aimd.ini`: +```ini +[SOMESCRIPTTYPE] ; ScriptType +x=19020,ID ; text +``` + +#### `19024` Play Speech + +- Similar to action 19000, but uses a string ID from the `[DialogList]` list instead of an index. + +In `aimd.ini`: +```ini +[SOMESCRIPTTYPE] ; ScriptType +x=19024,ID ; text +``` + +#### `19025` Play Sound + +- Similar to action 25, but uses a string ID from the `[SoundList]` list instead of an index. + +In `aimd.ini`: +```ini +[SOMESCRIPTTYPE] ; ScriptType +x=19025,ID ; text +``` + +#### `19027` Play Theme + +- Similar to action 27, but uses a string ID from the `[Themes]` list instead of an index. + +In `aimd.ini`: +```ini +[SOMESCRIPTTYPE] ; ScriptType +x=19027,ID ; text +``` + +#### `19051` Play Animation + +- Similar to action 51, but uses a string ID from the `[Animations]` list instead of an index. + +In `aimd.ini`: +```ini +[SOMESCRIPTTYPE] ; ScriptType +x=19051,ID ; text +``` + +#### `19046` Attack Enemy Structure + +- Similar to action 46, but uses a string ID from the `[BuildingTypes]` list instead of an index. +- The last parameter is the BwP. Check below the possible strings. + +In `aimd.ini`: +```ini +[SOMESCRIPTTYPE] ; ScriptType +x=19046,ID,BwP ; text +``` + +| *BwP values* | *Description* | +|--------------:|:----------------------------------------------------------| +| LeastThreat | Index of the instance of the building with least threat | +| HighestThreat | Index of the instance of the building with highest threat | +| Nearest | Index of the instance of the building which is nearest | +| Farthest | Index of the instance of the building which is farthest | + +```{note} +More BwP information in https://modenc.renegadeprojects.com/ScriptTypes/ScriptActions +For this action to work in multiplayer - you need to use a version of [Script Actions information at Modenc](https://modenc.renegadeprojects.com/ScriptTypes/ScriptActions). +``` + +#### `19047` Move To Enemy Structure + +- Similar to action 47, but uses a string ID from the `[BuildingTypes]` list instead of an index. +- The last parameter is the BwP. Check the table of the action `19046`. + +In `aimd.ini`: +```ini +[SOMESCRIPTTYPE] ; ScriptType +x=19047,ID,BwP ; text +``` + +#### `19056` Chronoshift TaskForce To Structure + +- Similar to action 56, but uses a string ID from the `[BuildingTypes]` list instead of an index. +- The last parameter is the BwP. Check the table of the action `19046`. + +In `aimd.ini`: +```ini +[SOMESCRIPTTYPE] ; ScriptType +x=19056,ID,BwP ; text +``` + +#### `19058` Move To Friendly Structure + +- Similar to action 58, but uses a string ID from the `[BuildingTypes]` list instead of an index. +- The last parameter is the BwP. Check the table of the action `19046`. + +In `aimd.ini`: +```ini +[SOMESCRIPTTYPE] ; ScriptType +x=19058,ID,BwP ; text +``` ## Trigger Actions diff --git a/docs/Whats-New.md b/docs/Whats-New.md index a50d94ae17..dd8ac258ec 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -451,6 +451,7 @@ New: - Allow the aircraft to enter area guard mission and not crash immediately without any airport (by CrimRecya) - [Unlimbo Detonate warhead](New-or-Enhanced-Logics.md#unlimbo-detonate-warhead) (by FlyStar) - Attack and damage technos underground (by TaranDahl) +- New ScriptTypeActions `19017, 19018, 19020, 19024, 19025, 19026, 19027, 19046, 19047, 19051, 19056, 19058` that require ID as argument (by FS-21) 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/Script/Body.h b/src/Ext/Script/Body.h index 49e955c9db..72d2b2616d 100644 --- a/src/Ext/Script/Body.h +++ b/src/Ext/Script/Body.h @@ -150,9 +150,22 @@ enum class PhobosScripts : unsigned int GlobalVariableReverseByGlobal = 18068, GlobalVariableXorByGlobal = 18069, GlobalVariableOrByGlobal = 18070, - GlobalVariableAndByGlobal = 18071 + GlobalVariableAndByGlobal = 18071, // Range 19000-19999 are miscellanous/uncategorized actions + PlaySpeech = 19000, // Reserved! PR 1900 needs to be merged + ChangeToScriptByID = 19017, + ChangeToTeamTypeByID = 19018, + ChangeToHouseByID = 19020, + PlaySpeechByID = 19024, + PlaySoundByID = 19025, + PlayMovieByID = 19026, // Reserved! Now does nothing + PlayThemeByID = 19027, + AttackEnemyStructureByID = 19046, + MoveToEnemyStructureByID = 19047, + PlayAnimationByID = 19051, + ChronoshiftTaskForceToStructureByID = 19056, + MoveToFriendlyStructureByID = 19058 }; class ScriptExt diff --git a/src/Ext/Script/Hooks.cpp b/src/Ext/Script/Hooks.cpp index 312952fbea..eeab98beb8 100644 --- a/src/Ext/Script/Hooks.cpp +++ b/src/Ext/Script/Hooks.cpp @@ -1,10 +1,19 @@ #include "Body.h" #include +#include #include #include +enum class BuildingWithProperty : unsigned int +{ + LeastThreat = 0, + HighestThreat = 65536, + Nearest = 131072, + Farthest = 196608 +}; + DEFINE_HOOK(0x6E9443, TeamClass_AI, 0x8) { GET(TeamClass*, pTeam, ESI); @@ -103,3 +112,144 @@ DEFINE_HOOK(0x6F01B0, TMission_ChronoShiftToTarget_SuperWeapons, 0x6) return SkipGameCode; } + +DEFINE_HOOK(0x723CA1, TeamMissionClass_FillIn_StringsSupport_and_id_masks, 0xB) +{ + enum { SkipCode = 0x723CD2 }; + + GET(ScriptActionNode*, node, ECX); + GET_STACK(char*, scriptActionLine, 0x8); + + int action = 0; + int argument = 0; + char* endptr; + + if (sscanf(scriptActionLine, "%d,%s", &action, Phobos::readBuffer) != 2) + { + node->Action = action; + node->Argument = argument; + R->ECX(node); + + return SkipCode; + } + + long val = strtol(Phobos::readBuffer, &endptr, 10); + + if (*endptr == '\0' + && val >= std::numeric_limits::min() + && val <= std::numeric_limits::max()) + { + // Integer case (the classic). + argument = static_cast(val); + } + else + { + // New strings case + char textArgument[sizeof(Phobos::readBuffer)] = { 0 }; + + action = action; + strcpy_s(textArgument, Phobos::readBuffer); + + // Action masks: These actions translate IDs into indices while preserving the original action values. + // The reason for using these masks is that some ScriptType actions rely on fixed indices rather than ID labels. + // When these lists change, there's a high probability of breaking the original index of the pointed element + char id[sizeof(AbstractTypeClass::ID)] = { 0 }; + char bwp[20] = { 0 }; + char* context = nullptr; + int index = 0; + int prefixIndex = 0; + + switch (static_cast(action)) + { + case PhobosScripts::ChangeToScriptByID: + action = 17; + index = ScriptTypeClass::FindIndex(textArgument); + break; + case PhobosScripts::ChangeToTeamTypeByID: + action = 18; + index = TeamTypeClass::FindIndex(textArgument); + break; + case PhobosScripts::ChangeToHouseByID: + action = 20; + index = HouseClass::FindIndexByName(textArgument); + break; + case PhobosScripts::PlaySpeechByID: // Note: PR 1900 needs to be merged into develop + action = static_cast(PhobosScripts::PlaySpeech); + index = VoxClass::FindIndex(textArgument); + break; + case PhobosScripts::PlaySoundByID: + action = 25; + index = VocClass::FindIndex(textArgument); + break; + case PhobosScripts::PlayMovieByID: + // Note: action "26" is currently impossible without an expert Phobos developer declaring the Movies class... in that case I could code the right FindIndex(textArgument) so sadly I'll skip "26" for now :-( + action = 26; + index = 0; + break; + case PhobosScripts::PlayThemeByID: + action = 27; + index = ThemeClass::Instance.FindIndex(textArgument); + break; + case PhobosScripts::PlayAnimationByID: + action = 51; + index = AnimTypeClass::FindIndex(textArgument); + break; + case PhobosScripts::AttackEnemyStructureByID: + case PhobosScripts::MoveToEnemyStructureByID: + case PhobosScripts::ChronoshiftTaskForceToStructureByID: + case PhobosScripts::MoveToFriendlyStructureByID: + if (PhobosScripts::AttackEnemyStructureByID == static_cast(action)) + action = 46; + else if (PhobosScripts::MoveToEnemyStructureByID == static_cast(action)) + action = 47; + else if (PhobosScripts::ChronoshiftTaskForceToStructureByID == static_cast(action)) + action = 56; + else if (PhobosScripts::MoveToFriendlyStructureByID == static_cast(action)) + action = 58; + + /* BwP check: + Information from https://modenc.renegadeprojects.com/ScriptTypes/ScriptActions + Computed Value Description + ------------------------------------- ------------------------------------------------------- + 0 (Hex 0x0) + Building Index -> Index of the instance of the building with least threat + 65536 (Hex 0x10000) + Building Index -> Index of the instance of the building with highest threat + 131072 (Hex 0x20000) + Building Index -> Index of the instance of the building which is nearest + 196608 (Hex 0x30000) + Building Index -> Index of the instance of the building which is farthest + */ + + //strcpy_s(id, strtok_s(textArgument, ",", &context)); + //_snprintf_s(bwp, sizeof(bwp), context); + //strcpy_s(bwp, context); + //context = nullptr; + //strcpy_s(bwp, strtok_s(textArgument, ",", &context)); + + if (sscanf(textArgument, "%[^,],%s", id, bwp) == 2) + { + index = BuildingTypeClass::FindIndex(id); + + if (index >= 0) + { + if (_strcmpi(bwp, "highestthreat") == 0) + prefixIndex = static_cast(BuildingWithProperty::HighestThreat); + else if (_strcmpi(bwp, "nearest") == 0) + prefixIndex = static_cast(BuildingWithProperty::Nearest); + else if (_strcmpi(bwp, "farthest") == 0) + prefixIndex = static_cast(BuildingWithProperty::Farthest); + } + } + break; + default: + index = 0; + break; + } + + if (index >= 0) + argument = prefixIndex + index; + } + + node->Action = action; + node->Argument = argument; + R->ECX(node); + + return SkipCode; +} From c5f7b131452e4e4dc56fe6c5539a29ba4e7af517 Mon Sep 17 00:00:00 2001 From: FS-21 Date: Thu, 9 Oct 2025 12:17:40 +0200 Subject: [PATCH 2/7] Updated --- docs/AI-Scripting-and-Mapping.md | 2 +- src/Ext/Script/Body.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/AI-Scripting-and-Mapping.md b/docs/AI-Scripting-and-Mapping.md index 51f2586ba6..b733bcdd94 100644 --- a/docs/AI-Scripting-and-Mapping.md +++ b/docs/AI-Scripting-and-Mapping.md @@ -524,7 +524,7 @@ x=19020,ID ; text #### `19024` Play Speech -- Similar to action 19000, but uses a string ID from the `[DialogList]` list instead of an index. +- Similar to action 24, but uses a string ID from the `[DialogList]` list instead of an index. In `aimd.ini`: ```ini diff --git a/src/Ext/Script/Body.h b/src/Ext/Script/Body.h index 72d2b2616d..704ec339f4 100644 --- a/src/Ext/Script/Body.h +++ b/src/Ext/Script/Body.h @@ -153,7 +153,6 @@ enum class PhobosScripts : unsigned int GlobalVariableAndByGlobal = 18071, // Range 19000-19999 are miscellanous/uncategorized actions - PlaySpeech = 19000, // Reserved! PR 1900 needs to be merged ChangeToScriptByID = 19017, ChangeToTeamTypeByID = 19018, ChangeToHouseByID = 19020, From 6a0e68ff5f30c2d47eb00988fc3d99f582a2b84c Mon Sep 17 00:00:00 2001 From: FS-21 Date: Thu, 9 Oct 2025 12:23:40 +0200 Subject: [PATCH 3/7] fix --- src/Ext/Script/Body.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ext/Script/Body.h b/src/Ext/Script/Body.h index 704ec339f4..000a303010 100644 --- a/src/Ext/Script/Body.h +++ b/src/Ext/Script/Body.h @@ -19,6 +19,8 @@ enum class PhobosScripts : unsigned int { + PlaySpeech = 24, // Reserved! PR 1900 needs to be merged + // Range 10000-10999 are team (aka ingame) actions // Sub-range 10000-10049 is for "attack" actions RepeatAttackCloser = 10000, From 9ba9e03efe5f860b0727989215f3d3412e18e6ba Mon Sep 17 00:00:00 2001 From: FS-21 Date: Sun, 12 Oct 2025 11:37:34 +0200 Subject: [PATCH 4/7] Fix string arguments with spaces --- src/Ext/Script/Hooks.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Ext/Script/Hooks.cpp b/src/Ext/Script/Hooks.cpp index eeab98beb8..ddf41427ef 100644 --- a/src/Ext/Script/Hooks.cpp +++ b/src/Ext/Script/Hooks.cpp @@ -124,7 +124,7 @@ DEFINE_HOOK(0x723CA1, TeamMissionClass_FillIn_StringsSupport_and_id_masks, 0xB) int argument = 0; char* endptr; - if (sscanf(scriptActionLine, "%d,%s", &action, Phobos::readBuffer) != 2) + if (sscanf(scriptActionLine, "%d,%[^\n]", &action, Phobos::readBuffer) != 2) { node->Action = action; node->Argument = argument; @@ -172,6 +172,9 @@ DEFINE_HOOK(0x723CA1, TeamMissionClass_FillIn_StringsSupport_and_id_masks, 0xB) case PhobosScripts::ChangeToHouseByID: action = 20; index = HouseClass::FindIndexByName(textArgument); + + if (index < 0) + ScriptExt::Log("AI Scripts - TeamMissionClass_FillIn_StringsSupport: Invalid House [%s]\n", textArgument); break; case PhobosScripts::PlaySpeechByID: // Note: PR 1900 needs to be merged into develop action = static_cast(PhobosScripts::PlaySpeech); From 1b5eb11b2bcc65b927fc60422a4c95641afd3ee3 Mon Sep 17 00:00:00 2001 From: FS-21 Date: Sun, 12 Oct 2025 23:08:04 +0200 Subject: [PATCH 5/7] Changed House conversion behaviour Instead of mentioning Houses now it should mention Countries --- src/Ext/Script/Hooks.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/Script/Hooks.cpp b/src/Ext/Script/Hooks.cpp index ddf41427ef..628e13dfea 100644 --- a/src/Ext/Script/Hooks.cpp +++ b/src/Ext/Script/Hooks.cpp @@ -171,10 +171,10 @@ DEFINE_HOOK(0x723CA1, TeamMissionClass_FillIn_StringsSupport_and_id_masks, 0xB) break; case PhobosScripts::ChangeToHouseByID: action = 20; - index = HouseClass::FindIndexByName(textArgument); + index = HouseTypeClass::FindIndexOfName(textArgument); if (index < 0) - ScriptExt::Log("AI Scripts - TeamMissionClass_FillIn_StringsSupport: Invalid House [%s]\n", textArgument); + ScriptExt::Log("AI Scripts - TeamMissionClass_FillIn_StringsSupport: Invalid Country string [%s]\n", textArgument); break; case PhobosScripts::PlaySpeechByID: // Note: PR 1900 needs to be merged into develop action = static_cast(PhobosScripts::PlaySpeech); From 69869f757ad57c2fa6c767f537b728aaf46a7d0f Mon Sep 17 00:00:00 2001 From: FS-21 Date: Sun, 12 Oct 2025 23:10:39 +0200 Subject: [PATCH 6/7] Updated docs regarding the 19020 action --- docs/AI-Scripting-and-Mapping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AI-Scripting-and-Mapping.md b/docs/AI-Scripting-and-Mapping.md index b733bcdd94..3b440156be 100644 --- a/docs/AI-Scripting-and-Mapping.md +++ b/docs/AI-Scripting-and-Mapping.md @@ -514,7 +514,7 @@ x=19018,ID ; text #### `19020` Change House -- Similar to action 20, but uses a string ID from the `[Houses]` list instead of an index. +- Similar to action 20, but uses a string ID from the `[Countries]` list instead of an index. In `aimd.ini`: ```ini From ec03819dbb644dd2a8137ce52ca84959a14833a5 Mon Sep 17 00:00:00 2001 From: FS-21 Date: Thu, 30 Oct 2025 12:42:45 +0100 Subject: [PATCH 7/7] Applied feedback --- src/Ext/Script/Hooks.cpp | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/Ext/Script/Hooks.cpp b/src/Ext/Script/Hooks.cpp index 628e13dfea..061a09d6b3 100644 --- a/src/Ext/Script/Hooks.cpp +++ b/src/Ext/Script/Hooks.cpp @@ -8,10 +8,10 @@ enum class BuildingWithProperty : unsigned int { - LeastThreat = 0, - HighestThreat = 65536, - Nearest = 131072, - Farthest = 196608 + LeastThreat = 0 << 16, + HighestThreat = 1 << 16, + Nearest = 2 << 16, + Farthest = 3 << 16 }; DEFINE_HOOK(0x6E9443, TeamClass_AI, 0x8) @@ -135,9 +135,7 @@ DEFINE_HOOK(0x723CA1, TeamMissionClass_FillIn_StringsSupport_and_id_masks, 0xB) long val = strtol(Phobos::readBuffer, &endptr, 10); - if (*endptr == '\0' - && val >= std::numeric_limits::min() - && val <= std::numeric_limits::max()) + if (*endptr == '\0') { // Integer case (the classic). argument = static_cast(val); @@ -176,7 +174,7 @@ DEFINE_HOOK(0x723CA1, TeamMissionClass_FillIn_StringsSupport_and_id_masks, 0xB) if (index < 0) ScriptExt::Log("AI Scripts - TeamMissionClass_FillIn_StringsSupport: Invalid Country string [%s]\n", textArgument); break; - case PhobosScripts::PlaySpeechByID: // Note: PR 1900 needs to be merged into develop + case PhobosScripts::PlaySpeechByID: action = static_cast(PhobosScripts::PlaySpeech); index = VoxClass::FindIndex(textArgument); break; @@ -220,12 +218,6 @@ DEFINE_HOOK(0x723CA1, TeamMissionClass_FillIn_StringsSupport_and_id_masks, 0xB) 196608 (Hex 0x30000) + Building Index -> Index of the instance of the building which is farthest */ - //strcpy_s(id, strtok_s(textArgument, ",", &context)); - //_snprintf_s(bwp, sizeof(bwp), context); - //strcpy_s(bwp, context); - //context = nullptr; - //strcpy_s(bwp, strtok_s(textArgument, ",", &context)); - if (sscanf(textArgument, "%[^,],%s", id, bwp) == 2) { index = BuildingTypeClass::FindIndex(id);