From c51b21bff5361061e91de00714f0283d65e3cf0f Mon Sep 17 00:00:00 2001 From: Starkku Date: Tue, 9 Sep 2025 21:33:08 +0300 Subject: [PATCH] Allow using as trigger owner in skirmish/MP --- CREDITS.md | 2 +- YRpp | 2 +- docs/AI-Scripting-and-Mapping.md | 1 + docs/Fixed-or-Improved-Logics.md | 1 - docs/Whats-New.md | 1 + src/Ext/Scenario/Body.cpp | 2 + src/Ext/Scenario/Body.h | 2 + src/Ext/Trigger/Hooks.cpp | 92 ++++++++++++++++++++++++++++++++ 8 files changed, 100 insertions(+), 3 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 599ca1b29a..570bcb67a4 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -268,7 +268,7 @@ This page lists all the individual contributions to the project by their author. - Build area customizations - `Scorch` / `Flamer` fire animation customization - EM Pulse cannon logic improvements - - `` as owner for pre-placed objects + - `` as owner for pre-placed objects and triggers - Custom exit cell for infantry factory - Vehicles keeping target on move command - `IsSonic` wave drawing crash fix diff --git a/YRpp b/YRpp index 5af96790ce..edc40e48e2 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 5af96790ce73e4ea068a390c60c124dccbc220e1 +Subproject commit edc40e48e2d815c4539eb200730018a5469e2d94 diff --git a/docs/AI-Scripting-and-Mapping.md b/docs/AI-Scripting-and-Mapping.md index e7a6f81cc5..b60cd2fe20 100644 --- a/docs/AI-Scripting-and-Mapping.md +++ b/docs/AI-Scripting-and-Mapping.md @@ -4,6 +4,7 @@ This page describes all AI scripting and mapping related additions and changes i ## Bugfixes and Miscellanous +- `` can now be used as owner for pre-placed objects as well as owner for triggers on skirmish and multiplayer maps. Triggers with owners that are not present in the game are destroyed and never sprung. - Script action `Move to cell` now obeys YR cell calculation now. Using `1000 * Y + X` as its cell value. (was `128 * Y + X` as it's a RA1 leftover) - The game now can reads waypoints ranges in [0, 2147483647]. (was [0,701]) - Map trigger action `41 Play Animation At...` can now create 'non-inert' animations which can play sounds, deal damage and apply `TiberiumChainReaction` if a parameter is set (needs [following changes to `fadata.ini`](Whats-New.md#for-map-editor-final-alert-2)). diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 11847189fd..4f5d799408 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -166,7 +166,6 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Certain global tileset indices (`ShorePieces`, `WaterSet`, `CliffSet`, `WaterCliffs`, `WaterBridge`, `BridgeSet` and `WoodBridgeSet`) can now be toggled to be parsed for lunar theater by setting `[General] -> ApplyLunarFixes` to true in `lunarmd.ini`. Do note that enabling this without fixing f.ex `WoodBridgeTileSet` pointing to a tileset with `TilesInSet=0` will cause issues in-game. - Fixed infantry `SecondaryFire` / `SecondaryProne` sequences being displayed in water instead of `WetAttack`. - Fixed objects with ally target and `AttackFriendlies=true` having their target reset every frame, particularly AI-owned buildings. -- `` can now be used as owner for pre-placed objects on skirmish and multiplayer maps. - Follower vehicle index for preplaced vehicles in maps is now explicitly constrained to `[Units]` list in map files and is no longer thrown off by vehicles that could not be created or created vehicles having other vehicles as initial passengers. - Drive/Jumpjet/Ship/Teleport locomotor did not power on when it is un-piggybacked bugfix - Stop command (`[S]` by default) behavior is now more correct: diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 432a86fe92..d07c9e34cb 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -443,6 +443,7 @@ New: - [Ammo-based deploy customizations for vehicles expanded to non-IsSimpleDeployer deploy functions](New-or-Enhanced-Logics.md#automatic-deploy-and-blocking-deploying-based-on-ammo) (by Starkku) - Randomized anims for several behaviors (by Ollerus) - Shield respawn animation and weapon (by Ollerus) +- `` can now be used as owner for triggers on skirmish and multiplayer maps (by Starkku) 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/Scenario/Body.cpp b/src/Ext/Scenario/Body.cpp index 7aedf6c3c2..f4f2ff442b 100644 --- a/src/Ext/Scenario/Body.cpp +++ b/src/Ext/Scenario/Body.cpp @@ -167,6 +167,7 @@ void ScenarioExt::ExtData::Serialize(T& Stm) .Process(this->DefaultLS800BkgdName) .Process(this->DefaultLS800BkgdPal) .Process(this->MasterDetonationBullet) + .Process(this->TriggerTypePlayerAtXOwners) // .Process(this->NewMessageList); // Should not S/L ; } @@ -195,6 +196,7 @@ DEFINE_HOOK(0x683549, ScenarioClass_CTOR, 0x9) ScenarioExt::Global()->Waypoints.clear(); ScenarioExt::Global()->Variables[0].clear(); ScenarioExt::Global()->Variables[1].clear(); + ScenarioExt::Global()->TriggerTypePlayerAtXOwners.clear(); return 0; } diff --git a/src/Ext/Scenario/Body.h b/src/Ext/Scenario/Body.h index af852d4892..818dc5f3b2 100644 --- a/src/Ext/Scenario/Body.h +++ b/src/Ext/Scenario/Body.h @@ -47,6 +47,7 @@ class ScenarioExt PhobosFixedString<64u> DefaultLS800BkgdPal; BulletClass* MasterDetonationBullet; // Used to do warhead/weapon detonations on spot without having to create new BulletClass instance every time. + std::map TriggerTypePlayerAtXOwners; // TriggerTypeClass ArrayIndex -> Player slot index ExtData(ScenarioClass* OwnerObject) : Extension(OwnerObject) , ShowBriefing { false } @@ -62,6 +63,7 @@ class ScenarioExt , DefaultLS800BkgdName {} , DefaultLS800BkgdPal {} , MasterDetonationBullet {} + , TriggerTypePlayerAtXOwners {} { } void SetVariableToByID(bool bIsGlobal, int nIndex, char bState); diff --git a/src/Ext/Trigger/Hooks.cpp b/src/Ext/Trigger/Hooks.cpp index 33e85a4683..57a6e2f6c1 100644 --- a/src/Ext/Trigger/Hooks.cpp +++ b/src/Ext/Trigger/Hooks.cpp @@ -1,7 +1,9 @@ #include +#include #include +#include #include DEFINE_HOOK(0x727064, TriggerTypeClass_HasLocalSetOrClearedEvent, 0x5) @@ -27,3 +29,93 @@ DEFINE_HOOK(0x727024, TriggerTypeClass_HasGlobalSetOrClearedEvent, 0x5) ? 0x72702E : 0x727029; } + +#pragma region PlayerAtX + +// Store player slot index for trigger type if such value is used in scenario INI. +DEFINE_HOOK(0x727292, TriggerTypeClass_ReadINI_PlayerAtX, 0x5) +{ + GET(TriggerTypeClass*, pThis, EBP); + GET(const char*, pID, ESI); + + // Bail out early in campaign mode or if the name does not start with < + if (SessionClass::IsCampaign() || *pID != '<') + return 0; + + const int playerAtIndex = HouseClass::GetPlayerAtFromString(pID); + + if (playerAtIndex != -1) + { + ScenarioExt::Global()->TriggerTypePlayerAtXOwners.emplace(pThis->ArrayIndex, playerAtIndex); + + // Override the name to prevent Ares whining about non-existing HouseType names. + R->ESI(NONE_STR); + } + + return 0; +} + +// Handle mapping player slot index for trigger to HouseClass pointer in logic. +DEFINE_HOOK_AGAIN(0x7265F7, TriggerClass_Logic_PlayerAtX, 0x6) +DEFINE_HOOK(0x72652D, TriggerClass_Logic_PlayerAtX, 0x6) +{ + enum { SkipGameCode1 = 0x726538, SkipGameCode2 = 0x726602}; + + GET(TriggerTypeClass*, pType, EDX); + + if (SessionClass::IsCampaign()) + return 0; + + auto const& triggerOwners = ScenarioExt::Global()->TriggerTypePlayerAtXOwners; + auto it = triggerOwners.find(pType->ArrayIndex); + + if (it != triggerOwners.end()) + { + if (auto const pHouse = HouseClass::FindByPlayerAt(it->second)) + { + R->EAX(pHouse); + return R->Origin() == 0x72652D ? SkipGameCode1 : SkipGameCode2; + } + } + + return 0; +} + +// Destroy triggers with Player @ X owners if they are not present in scenario. +DEFINE_HOOK(0x725FC7, TriggerClass_CTOR_PlayerAtX, 0x7) +{ + GET(TriggerClass*, pThis, ESI); + + if (SessionClass::IsCampaign()) + return 0; + + auto& triggerOwners = ScenarioExt::Global()->TriggerTypePlayerAtXOwners; + auto it = triggerOwners.find(pThis->Type->ArrayIndex); + + if (it != triggerOwners.end()) + { + if (!HouseClass::FindByPlayerAt(it->second)) + pThis->Destroy(); + } + + return 0; +} + +// Remove destroyed triggers from the map. +DEFINE_HOOK(0x726727, TriggerClass_Destroy_PlayerAtX, 0x5) +{ + GET(TriggerClass*, pThis, ESI); + + if (SessionClass::IsCampaign()) + return 0; + + auto& triggerOwners = ScenarioExt::Global()->TriggerTypePlayerAtXOwners; + auto it = triggerOwners.find(pThis->Type->ArrayIndex); + + if (it != triggerOwners.end()) + triggerOwners.erase(it); + + return 0; +} + +#pragma endregion