diff --git a/CREDITS.md b/CREDITS.md index 3214917531..59ca914341 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..3b440156be 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 `[Countries]` list instead of an index. + +In `aimd.ini`: +```ini +[SOMESCRIPTTYPE] ; ScriptType +x=19020,ID ; text +``` + +#### `19024` Play Speech + +- Similar to action 24, 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 8c7502aad5..c2d30c458c 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..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, @@ -150,9 +152,21 @@ enum class PhobosScripts : unsigned int GlobalVariableReverseByGlobal = 18068, GlobalVariableXorByGlobal = 18069, GlobalVariableOrByGlobal = 18070, - GlobalVariableAndByGlobal = 18071 + GlobalVariableAndByGlobal = 18071, // Range 19000-19999 are miscellanous/uncategorized actions + 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..061a09d6b3 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 << 16, + HighestThreat = 1 << 16, + Nearest = 2 << 16, + Farthest = 3 << 16 +}; + DEFINE_HOOK(0x6E9443, TeamClass_AI, 0x8) { GET(TeamClass*, pTeam, ESI); @@ -103,3 +112,139 @@ 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,%[^\n]", &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') + { + // 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 = HouseTypeClass::FindIndexOfName(textArgument); + + if (index < 0) + ScriptExt::Log("AI Scripts - TeamMissionClass_FillIn_StringsSupport: Invalid Country string [%s]\n", textArgument); + break; + case PhobosScripts::PlaySpeechByID: + 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 + */ + + 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; +}