From 6cc1a65e99db42307c99066d33b14b40d1e6e2cb Mon Sep 17 00:00:00 2001 From: FS-21 Date: Tue, 27 May 2025 12:59:36 +0200 Subject: [PATCH 1/8] Initial commit --- CREDITS.md | 1 + docs/User-Interface.md | 18 ++++++++ docs/Whats-New.md | 1 + src/Ext/Building/Body.cpp | 80 +++++++++++++++++++++++++++++++++++ src/Ext/Building/Body.h | 1 + src/Ext/Building/Hooks.cpp | 8 ++++ src/Ext/BuildingType/Body.cpp | 11 +++++ src/Ext/BuildingType/Body.h | 11 +++++ src/Ext/Scenario/Body.cpp | 43 ++++++++++++++++++- src/Ext/Scenario/Body.h | 3 ++ src/Ext/Side/Body.cpp | 6 +++ src/Ext/Side/Body.h | 2 + 12 files changed, 184 insertions(+), 1 deletion(-) diff --git a/CREDITS.md b/CREDITS.md index 1fcf47b07d..053653e6f3 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -141,6 +141,7 @@ This page lists all the individual contributions to the project by their author. - Map Events 604 & 605 for checking if a specific Techno enters in a cell - Warhead that can not kill - `Pips.HideIfNoStrength` and `SelfHealing.EnabledBy` additions for shields + - New EVA voice after deploying a building - **Starkku**: - Misc. minor bugfixes & improvements - AI script actions: diff --git a/docs/User-Interface.md b/docs/User-Interface.md index a07fd43cbc..f9d402b25e 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -27,6 +27,24 @@ IngameScore.WinTheme= ; Soundtrack theme ID IngameScore.LoseTheme= ; Soundtrack theme ID ``` +### New EVA voice after deploying a building + +- You can now replace the current EVA voice when a specific structure is placed/deployed. +- `NewEVAVoice.Index` is the index of the new EVA voice. Ares is hightly recomended because these indexes are readed the new section `[EVATypes]` at `evamd.ini` created by Ares. Look at Ares documentation for more information. +- In case of multiple structures with different EVA voices `NewEVAVoice.Priority` establish a priority queue, being the highest value the selected one. +- `NewEVAVoice.RecheckOnDeath` re-checks a new EVA voice after the destruction/undeployment of of of these buildings. +- `NewEVAVoice.InitialMessage` plays an EVA message to the player when a different EVA has been selected. + +In `rulesmd.ini`: +```ini +[SOMEBUILDING] ; BuildingType +NewEVAVoice= ; boolean +NewEVAVoice.Index=0 ; integer +NewEVAVoice.Priority=1 ; integer +NewEVAVoice.RecheckOnDeath=false ; boolean +NewEVAVoice.InitialMessage= ; EVA entry +``` + ## Battle screen UI/UX ### Digital display diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 3077b62cf7..11fcb1bbfa 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -380,6 +380,7 @@ New: - Dehardcoded 255 limit of `OverlayType` (by secsome) - `Strafing` is now disabled by default when using `Trajectory` (by CrimRecya) - [Customizable airstrike flare colors](Fixed-or-Improved-Logics.md#airstrike-flare-customizations) (by Starkku) +- New EVA voice after deploying a building (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/Building/Body.cpp b/src/Ext/Building/Body.cpp index ffa3f1c6e0..fe4bb7c7f3 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -3,6 +3,9 @@ #include #include +#include +#include +#include BuildingExt::ExtContainer BuildingExt::ExtMap; @@ -446,6 +449,83 @@ const std::vector BuildingExt::GetFoundationCells(BuildingClass* con return foundationCells; } +void BuildingExt::ExtData::UpdateMainEvaVoice() +{ + auto const pThis = this->OwnerObject(); + auto const pTypeExt = this->TypeExtData; + + if (!pTypeExt->NewEvaVoice.Get(false)) + return; + + auto const pHouse = pThis->Owner; + int newPriority = -1; + int newEvaIndex = VoxClass::EVAIndex; + + for (const auto pBuilding : pHouse->Buildings) + { + if (pBuilding->CurrentMission == Mission::Selling) + continue; + + auto const pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pBuilding->Type); + + // The first highest priority takes precedence over lower ones + if (pBuildingTypeExt->NewEvaVoice.Get(false) && pBuildingTypeExt->NewEvaVoice_Priority > newPriority) + { + newPriority = pBuildingTypeExt->NewEvaVoice_Priority; + newEvaIndex = pBuildingTypeExt->NewEvaVoice_Index; + } + } + + if (pThis->CurrentMission != Mission::Selling && pTypeExt->NewEvaVoice_Priority > newPriority) + { + newPriority = pTypeExt->NewEvaVoice_Priority; + newEvaIndex = pTypeExt->NewEvaVoice_Index; + } + + if (newPriority > 0 && VoxClass::EVAIndex != newEvaIndex) + { + // Note: if the index points to a nonexistant voice index then the player will hear no EVA voices + VoxClass::EVAIndex = newEvaIndex; + + // Greeting of the new EVA voice + int idxPlay = pTypeExt->NewEvaVoice_InitialMessage.Get(-1); + + if (idxPlay != -1) + VoxClass::PlayIndex(idxPlay); + } + else if (newPriority < 0) + { + // Restore the original EVA voice of the owner's side + SideClass* pSide = SideClass::Array.GetItem(pHouse->SideIndex); + auto pSideExt = SideExt::ExtMap.Find(pSide); + int idxEVA = 0; // By default "Allies" EVA voice + + if (pSideExt->EVA_Tag.isset()) + { + auto evaTag = pSideExt->EVA_Tag.Get(); + + for (size_t i = 0; i < ScenarioExt::Global()->EVAIndexList.size(); i++) + { + auto item = ScenarioExt::Global()->EVAIndexList[i]; + + if (_strcmpi(item, evaTag) == 0) + { + idxEVA = i; + break; + } + } + } + + // "Allied" & "none" cases share the same index 0 + if (pHouse->SideIndex == 1) + idxEVA = 1; // Russian + else if (pHouse->SideIndex == 2) + idxEVA = 2; // Yuri + + VoxClass::EVAIndex = idxEVA; + } +} + // ============================= // load / save diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index 6d0d562907..e9620df2c1 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -59,6 +59,7 @@ class BuildingExt bool HasSuperWeapon(int index, bool withUpgrades) const; bool HandleInfiltrate(HouseClass* pInfiltratorHouse, int moneybefore); void UpdatePrimaryFactoryAI(); + void UpdateMainEvaVoice(); virtual ~ExtData() = default; // virtual void LoadFromINIFile(CCINIClass* pINI) override; diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index 153bf071d7..d714ae0f14 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -251,6 +251,11 @@ DEFINE_HOOK(0x440B4F, BuildingClass_Unlimbo_SetShouldRebuild, 0x5) enum { ContinueCheck = 0x440B58, SkipSetShouldRebuild = 0x440B81 }; GET(BuildingClass* const, pThis, ESI); + auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + + if (pTypeExt->NewEvaVoice.Get(false)) + BuildingExt::ExtMap.Find(pThis)->UpdateMainEvaVoice(); + if (SessionClass::IsCampaign()) { // Preplaced structures are already managed before @@ -435,6 +440,9 @@ DEFINE_HOOK(0x445D87, BuildingClass_Limbo_DestroyableObstacle, 0x6) } } + if (pTypeExt->NewEvaVoice.Get(false) && pTypeExt->NewEvaVoice_RecheckOnDeath) + BuildingExt::ExtMap.Find(pThis)->UpdateMainEvaVoice(); + return 0; } diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index 632b0e1887..16bb008c18 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -179,6 +179,12 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Overpower_KeepOnline.Read(exINI, pSection, "Overpower.KeepOnline"); this->Overpower_ChargeWeapon.Read(exINI, pSection, "Overpower.ChargeWeapon"); + this->NewEvaVoice.Read(exINI, pSection, "NewEVAVoice"); + this->NewEvaVoice_Index.Read(exINI, pSection, "NewEVAVoice.Index"); + this->NewEvaVoice_Priority.Read(exINI, pSection, "NewEVAVoice.Priority"); + this->NewEvaVoice_RecheckOnDeath.Read(exINI, pSection, "NewEVAVoice.RecheckOnDeath"); + this->NewEvaVoice_InitialMessage.Read(exINI, pSection, "NewEVAVoice.InitialMessage"); + if (pThis->NumberOfDocks > 0) { this->AircraftDockingDirs.clear(); @@ -305,6 +311,11 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm) .Process(this->BarracksExitCell) .Process(this->Overpower_KeepOnline) .Process(this->Overpower_ChargeWeapon) + .Process(this->NewEvaVoice) + .Process(this->NewEvaVoice_Index) + .Process(this->NewEvaVoice_Priority) + .Process(this->NewEvaVoice_RecheckOnDeath) + .Process(this->NewEvaVoice_InitialMessage) ; } diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 74d0e27e4c..3cc947d5cc 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -84,6 +84,12 @@ class BuildingTypeExt Valueable Overpower_KeepOnline; Valueable Overpower_ChargeWeapon; + Nullable NewEvaVoice; + Valueable NewEvaVoice_Index; + Valueable NewEvaVoice_Priority; + Valueable NewEvaVoice_RecheckOnDeath; + NullableIdx NewEvaVoice_InitialMessage; + ExtData(BuildingTypeClass* OwnerObject) : Extension(OwnerObject) , PowersUp_Owner { AffectedHouse::Owner } , PowersUp_Buildings {} @@ -135,6 +141,11 @@ class BuildingTypeExt , BarracksExitCell {} , Overpower_KeepOnline { 2 } , Overpower_ChargeWeapon { 1 } + , NewEvaVoice {} + , NewEvaVoice_Index { 0 } + , NewEvaVoice_Priority { 0 } + , NewEvaVoice_RecheckOnDeath { false } + , NewEvaVoice_InitialMessage { } { } // Ares 0.A functions diff --git a/src/Ext/Scenario/Body.cpp b/src/Ext/Scenario/Body.cpp index 0b69f98bc6..53e91bca83 100644 --- a/src/Ext/Scenario/Body.cpp +++ b/src/Ext/Scenario/Body.cpp @@ -147,7 +147,7 @@ void ScenarioExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->ShowBriefing = ini_missionmd.ReadBool(scenarioName, "ShowBriefing", pINI->ReadBool(GameStrings::Basic, "ShowBriefing", this->ShowBriefing)); this->BriefingTheme = ini_missionmd.ReadTheme(scenarioName, "BriefingTheme", pINI->ReadTheme(GameStrings::Basic, "BriefingTheme", this->BriefingTheme)); - + } } @@ -162,6 +162,7 @@ void ScenarioExt::ExtData::Serialize(T& Stm) .Process(this->BriefingTheme) .Process(this->AutoDeathObjects) .Process(this->TransportReloaders) + .Process(this->EVAIndexList) ; } @@ -262,3 +263,43 @@ DEFINE_HOOK(0x55B4E1, LogicClass_Update_BeforeAll, 0x5) return 0; } + +// Reading Ares [EVATypes] section in evamd.ini +// Address 0x753000 is used by Ares and replaces the whole function VoxClass::CreateFromINIList so this address must be used +DEFINE_HOOK(0x752DCC, VoxClass_ReadINI_EVATypes, 0x7) +{ + GET(CCINIClass* const, pINI, ECX); + + ScenarioExt::Global()->EVAIndexList.clear(); + const auto pSection = "EVATypes"; + + if (pINI->GetSection(pSection)) + { + auto const count = pINI->GetKeyCount(pSection); + + // The first EVA voices from the original game + ScenarioExt::Global()->EVAIndexList.emplace_back(_strdup(GameStrings::Allied)); + ScenarioExt::Global()->EVAIndexList.emplace_back(_strdup(GameStrings::Russian)); + ScenarioExt::Global()->EVAIndexList.emplace_back(_strdup(GameStrings::Yuri)); + + // New EVA voices + for (auto i = 0; i < count; ++i) + { + auto const pKey = pINI->GetKeyName(pSection, i); + if (pINI->ReadString(pSection, pKey, "", Phobos::readBuffer) > 0) + { + bool found = std::find(ScenarioExt::Global()->EVAIndexList.begin(), ScenarioExt::Global()->EVAIndexList.end(), Phobos::readBuffer) != ScenarioExt::Global()->EVAIndexList.end(); + + if (!found) + { + char* str = _strdup(Phobos::readBuffer); + ScenarioExt::Global()->EVAIndexList.emplace_back(str); + free(str); + } + } + } + } + + return 0; +} + diff --git a/src/Ext/Scenario/Body.h b/src/Ext/Scenario/Body.h index 3d296a5c5f..51dfa5b191 100644 --- a/src/Ext/Scenario/Body.h +++ b/src/Ext/Scenario/Body.h @@ -36,6 +36,8 @@ class ScenarioExt std::vector AutoDeathObjects; std::vector TransportReloaders; // Objects that can reload ammo in limbo + std::vector EVAIndexList; + ExtData(ScenarioClass* OwnerObject) : Extension(OwnerObject) , ShowBriefing { false } , BriefingTheme { -1 } @@ -43,6 +45,7 @@ class ScenarioExt , Variables { } , AutoDeathObjects {} , TransportReloaders {} + , EVAIndexList {} { } void SetVariableToByID(bool bIsGlobal, int nIndex, char bState); diff --git a/src/Ext/Side/Body.cpp b/src/Ext/Side/Body.cpp index d763574cb1..e954aaf442 100644 --- a/src/Ext/Side/Body.cpp +++ b/src/Ext/Side/Body.cpp @@ -42,6 +42,11 @@ void SideExt::ExtData::LoadFromINIFile(CCINIClass* pINI) this->ToolTip_Background_Opacity.Read(exINI, pSection, "ToolTip.Background.Opacity"); this->ToolTip_Background_BlurSize.Read(exINI, pSection, "ToolTip.Background.BlurSize"); this->BriefingTheme = pINI->ReadTheme(pSection, "BriefingTheme", this->BriefingTheme); + + pINI->ReadString(pSection, "EVA.Tag", "", Phobos::readBuffer); + + if (std::strlen(Phobos::readBuffer) > 0) + this->EVA_Tag = _strdup(Phobos::readBuffer); } // ============================= @@ -71,6 +76,7 @@ void SideExt::ExtData::Serialize(T& Stm) .Process(this->IngameScore_WinTheme) .Process(this->IngameScore_LoseTheme) .Process(this->BriefingTheme) + .Process(this->EVA_Tag) ; } diff --git a/src/Ext/Side/Body.h b/src/Ext/Side/Body.h index 88e694aa0c..b5083131c5 100644 --- a/src/Ext/Side/Body.h +++ b/src/Ext/Side/Body.h @@ -36,6 +36,7 @@ class SideExt Nullable ToolTip_Background_Opacity; Nullable ToolTip_Background_BlurSize; Valueable BriefingTheme; + Nullable EVA_Tag; ExtData(SideClass* OwnerObject) : Extension(OwnerObject) , ArrayIndex { -1 } @@ -58,6 +59,7 @@ class SideExt , ToolTip_Background_Opacity { } , ToolTip_Background_BlurSize { } , BriefingTheme { -1 } + , EVA_Tag { } { } virtual ~ExtData() = default; From bcdc4299a89e43f5e74443e8a3ba018023176567 Mon Sep 17 00:00:00 2001 From: FS-21 Date: Tue, 27 May 2025 13:01:10 +0200 Subject: [PATCH 2/8] YRpp update added 4 new GameStrings --- YRpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YRpp b/YRpp index c2133762cb..afe7d27c45 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit c2133762cbea0be1be37eae38108d0e65ad33fd4 +Subproject commit afe7d27c457ac06e2323f73875394708c84f4db0 From a81dfc4fe2bd4ffda7bb8d8fa7902a323e1c9b66 Mon Sep 17 00:00:00 2001 From: FS-21 Date: Tue, 27 May 2025 13:02:50 +0200 Subject: [PATCH 3/8] deleted a white space --- src/Ext/Scenario/Body.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Ext/Scenario/Body.cpp b/src/Ext/Scenario/Body.cpp index 53e91bca83..38c8a2dfb6 100644 --- a/src/Ext/Scenario/Body.cpp +++ b/src/Ext/Scenario/Body.cpp @@ -147,7 +147,6 @@ void ScenarioExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->ShowBriefing = ini_missionmd.ReadBool(scenarioName, "ShowBriefing", pINI->ReadBool(GameStrings::Basic, "ShowBriefing", this->ShowBriefing)); this->BriefingTheme = ini_missionmd.ReadTheme(scenarioName, "BriefingTheme", pINI->ReadTheme(GameStrings::Basic, "BriefingTheme", this->BriefingTheme)); - } } From ff67beb588bc22cdacf4eae035fe6318e37a6045 Mon Sep 17 00:00:00 2001 From: FS-21 Date: Wed, 28 May 2025 08:40:40 +0200 Subject: [PATCH 4/8] Fixes - The player will hear only his own EVA voices from the tag NewEVAVoice.InitialMessage. - The readed data of the [EVATypes] was deleted before it was used by the new code. - Small fixes & tweaks. --- src/Ext/Building/Body.cpp | 43 +++++++++++++++++++-------------- src/Ext/Rules/Body.cpp | 51 +++++++++++++++++++++++++++++++++++++++ src/Ext/Rules/Body.h | 5 ++++ src/Ext/Scenario/Body.cpp | 41 ------------------------------- src/Ext/Scenario/Body.h | 3 --- 5 files changed, 81 insertions(+), 62 deletions(-) diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index fe4bb7c7f3..c388964c6f 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -451,18 +451,23 @@ const std::vector BuildingExt::GetFoundationCells(BuildingClass* con void BuildingExt::ExtData::UpdateMainEvaVoice() { - auto const pThis = this->OwnerObject(); - auto const pTypeExt = this->TypeExtData; + const auto pTypeExt = this->TypeExtData; if (!pTypeExt->NewEvaVoice.Get(false)) return; - auto const pHouse = pThis->Owner; + const auto pThis = this->OwnerObject(); + const auto pHouse = pThis->Owner; + + if (!pHouse->IsControlledByCurrentPlayer()) + return; + int newPriority = -1; int newEvaIndex = VoxClass::EVAIndex; for (const auto pBuilding : pHouse->Buildings) { + // Special case that must be avoided here because can be the current EVA changer if (pBuilding->CurrentMission == Mission::Selling) continue; @@ -484,7 +489,6 @@ void BuildingExt::ExtData::UpdateMainEvaVoice() if (newPriority > 0 && VoxClass::EVAIndex != newEvaIndex) { - // Note: if the index points to a nonexistant voice index then the player will hear no EVA voices VoxClass::EVAIndex = newEvaIndex; // Greeting of the new EVA voice @@ -496,33 +500,36 @@ void BuildingExt::ExtData::UpdateMainEvaVoice() else if (newPriority < 0) { // Restore the original EVA voice of the owner's side - SideClass* pSide = SideClass::Array.GetItem(pHouse->SideIndex); - auto pSideExt = SideExt::ExtMap.Find(pSide); - int idxEVA = 0; // By default "Allies" EVA voice + const auto pSide = SideClass::Array.GetItem(pHouse->SideIndex); + const auto pSideExt = SideExt::ExtMap.Find(pSide); + newEvaIndex = 0; // By default is set the "Allies" EVA voice if (pSideExt->EVA_Tag.isset()) { - auto evaTag = pSideExt->EVA_Tag.Get(); + const auto evaTag = pSideExt->EVA_Tag.Get(); - for (size_t i = 0; i < ScenarioExt::Global()->EVAIndexList.size(); i++) + for (size_t i = 0; i < RulesExt::Global()->EVAIndexList.size(); i++) { - auto item = ScenarioExt::Global()->EVAIndexList[i]; + const auto item = RulesExt::Global()->EVAIndexList[i].c_str(); if (_strcmpi(item, evaTag) == 0) { - idxEVA = i; + newEvaIndex = i; break; } } } + else + { + if (pHouse->SideIndex == 0) + newEvaIndex = 0; // Allied + if (pHouse->SideIndex == 1) + newEvaIndex = 1; // Russian + else if (pHouse->SideIndex == 2) + newEvaIndex = 2; // Yuri + } - // "Allied" & "none" cases share the same index 0 - if (pHouse->SideIndex == 1) - idxEVA = 1; // Russian - else if (pHouse->SideIndex == 2) - idxEVA = 2; // Yuri - - VoxClass::EVAIndex = idxEVA; + VoxClass::EVAIndex = newEvaIndex; } } diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 6f2b133a7a..b4293e8baf 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -268,6 +268,9 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->HarvesterScanAfterUnload.Read(exINI, GameStrings::General, "HarvesterScanAfterUnload"); + // Reading Ares section [EVATypes] at evamd.ini + LoadEvaVoices(); + // Section AITargetTypes int itemsCount = pINI->GetKeyCount("AITargetTypes"); for (int i = 0; i < itemsCount; ++i) @@ -307,6 +310,10 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) } } +namespace EVAIndexListTemp +{ + std::vector EVAIndexList; +} // this runs between the before and after type data loading methods for rules ini void RulesExt::ExtData::InitializeAfterTypeData(RulesClass* const pThis) { @@ -491,6 +498,7 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->ProneSpeed_NoCrawls) .Process(this->DamagedSpeed) .Process(this->HarvesterScanAfterUnload) + .Process(this->EVAIndexList) ; } @@ -533,6 +541,49 @@ void RulesExt::ExtData::ReplaceVoxelLightSources() Game::DestroyVoxelCaches(); } +void RulesExt::ExtData::LoadEvaVoices() +{ + CCFileClass pEvamdFile("evamd.ini"); + + if (pEvamdFile.Exists() && pEvamdFile.Open(FileAccessMode::Read)) + { + CCINIClass iniEva; + iniEva.ReadCCFile(&pEvamdFile, true); + iniEva.CurrentSection = nullptr; + iniEva.CurrentSectionName = nullptr; + const auto pEvaSection = "EVATypes"; + + if (iniEva.GetSection(pEvaSection)) + { + this->EVAIndexList.clear(); + + // Default EVA voices + this->EVAIndexList.emplace_back(GameStrings::Allied); + this->EVAIndexList.emplace_back(GameStrings::Russian); + this->EVAIndexList.emplace_back(GameStrings::Yuri); + + // New EVA voices due to a new Ares section in evamd.ini + const auto count = iniEva.GetKeyCount(pEvaSection); + + for (std::size_t i = 0; i < count; i++) + { + const auto pEvaKey = iniEva.GetKeyName(pEvaSection, i); + + if (iniEva.ReadString(pEvaSection, pEvaKey, "", Phobos::readBuffer) > 0) + { + std::string buffer = Phobos::readBuffer; + bool found = std::find(this->EVAIndexList.begin(), this->EVAIndexList.end(), buffer) != this->EVAIndexList.end(); + + if (!found) + this->EVAIndexList.emplace_back(buffer); + } + } + } + } + + pEvamdFile.Close(); +} + // ============================= // container hooks diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index f8e85d27a5..52daf8c1b9 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -221,6 +221,8 @@ class RulesExt Valueable HarvesterScanAfterUnload; + std::vector EVAIndexList; + ExtData(RulesClass* OwnerObject) : Extension(OwnerObject) , Storage_TiberiumIndex { -1 } , HarvesterDumpAmount { 0.0f } @@ -387,6 +389,8 @@ class RulesExt , DamagedSpeed { 0.75 } , HarvesterScanAfterUnload { false } + + , EVAIndexList {} { } virtual ~ExtData() = default; @@ -403,6 +407,7 @@ class RulesExt virtual void SaveToStream(PhobosStreamWriter& Stm) override; void ReplaceVoxelLightSources(); + void LoadEvaVoices(); private: template diff --git a/src/Ext/Scenario/Body.cpp b/src/Ext/Scenario/Body.cpp index 38c8a2dfb6..52f03becee 100644 --- a/src/Ext/Scenario/Body.cpp +++ b/src/Ext/Scenario/Body.cpp @@ -161,7 +161,6 @@ void ScenarioExt::ExtData::Serialize(T& Stm) .Process(this->BriefingTheme) .Process(this->AutoDeathObjects) .Process(this->TransportReloaders) - .Process(this->EVAIndexList) ; } @@ -262,43 +261,3 @@ DEFINE_HOOK(0x55B4E1, LogicClass_Update_BeforeAll, 0x5) return 0; } - -// Reading Ares [EVATypes] section in evamd.ini -// Address 0x753000 is used by Ares and replaces the whole function VoxClass::CreateFromINIList so this address must be used -DEFINE_HOOK(0x752DCC, VoxClass_ReadINI_EVATypes, 0x7) -{ - GET(CCINIClass* const, pINI, ECX); - - ScenarioExt::Global()->EVAIndexList.clear(); - const auto pSection = "EVATypes"; - - if (pINI->GetSection(pSection)) - { - auto const count = pINI->GetKeyCount(pSection); - - // The first EVA voices from the original game - ScenarioExt::Global()->EVAIndexList.emplace_back(_strdup(GameStrings::Allied)); - ScenarioExt::Global()->EVAIndexList.emplace_back(_strdup(GameStrings::Russian)); - ScenarioExt::Global()->EVAIndexList.emplace_back(_strdup(GameStrings::Yuri)); - - // New EVA voices - for (auto i = 0; i < count; ++i) - { - auto const pKey = pINI->GetKeyName(pSection, i); - if (pINI->ReadString(pSection, pKey, "", Phobos::readBuffer) > 0) - { - bool found = std::find(ScenarioExt::Global()->EVAIndexList.begin(), ScenarioExt::Global()->EVAIndexList.end(), Phobos::readBuffer) != ScenarioExt::Global()->EVAIndexList.end(); - - if (!found) - { - char* str = _strdup(Phobos::readBuffer); - ScenarioExt::Global()->EVAIndexList.emplace_back(str); - free(str); - } - } - } - } - - return 0; -} - diff --git a/src/Ext/Scenario/Body.h b/src/Ext/Scenario/Body.h index 51dfa5b191..3d296a5c5f 100644 --- a/src/Ext/Scenario/Body.h +++ b/src/Ext/Scenario/Body.h @@ -36,8 +36,6 @@ class ScenarioExt std::vector AutoDeathObjects; std::vector TransportReloaders; // Objects that can reload ammo in limbo - std::vector EVAIndexList; - ExtData(ScenarioClass* OwnerObject) : Extension(OwnerObject) , ShowBriefing { false } , BriefingTheme { -1 } @@ -45,7 +43,6 @@ class ScenarioExt , Variables { } , AutoDeathObjects {} , TransportReloaders {} - , EVAIndexList {} { } void SetVariableToByID(bool bIsGlobal, int nIndex, char bState); From b3e31ba1d5d40b4f288008afd05513411b4e3111 Mon Sep 17 00:00:00 2001 From: FS-21 Date: Wed, 28 May 2025 09:19:05 +0200 Subject: [PATCH 5/8] YRpp --- YRpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YRpp b/YRpp index afe7d27c45..025c99e4fd 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit afe7d27c457ac06e2323f73875394708c84f4db0 +Subproject commit 025c99e4fd12640da5476e7126bd22438c9cf78e From 8ffb3a1b73a35f6fc49c560acc2929a340e6aaaf Mon Sep 17 00:00:00 2001 From: FS-21 Date: Tue, 8 Jul 2025 08:19:12 +0200 Subject: [PATCH 6/8] Applied feedback - Removed NewEvaVoice tag. - NewEvaVoice.Index now is nullable int and the checks are against this value instead of the old NewEvaVoice. --- YRpp | 2 +- src/Ext/Building/Body.cpp | 10 +++++----- src/Ext/Building/Hooks.cpp | 12 ++++++++---- src/Ext/BuildingType/Body.cpp | 2 -- src/Ext/BuildingType/Body.h | 6 ++---- src/Ext/Rules/Body.cpp | 4 ---- 6 files changed, 16 insertions(+), 20 deletions(-) diff --git a/YRpp b/YRpp index 4e67ea43a1..3e4db25bb7 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 4e67ea43a108570ae2bebc5fb0bbd75dfce23221 +Subproject commit 3e4db25bb775d44ec896392bcded614628452c2d diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index c388964c6f..c542c9b222 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -453,7 +453,7 @@ void BuildingExt::ExtData::UpdateMainEvaVoice() { const auto pTypeExt = this->TypeExtData; - if (!pTypeExt->NewEvaVoice.Get(false)) + if (!pTypeExt->NewEvaVoice_Index.isset()) return; const auto pThis = this->OwnerObject(); @@ -467,14 +467,14 @@ void BuildingExt::ExtData::UpdateMainEvaVoice() for (const auto pBuilding : pHouse->Buildings) { + const auto pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pBuilding->Type); + // Special case that must be avoided here because can be the current EVA changer - if (pBuilding->CurrentMission == Mission::Selling) + if (!pBuildingTypeExt->NewEvaVoice_Index.isset() || pBuilding->CurrentMission == Mission::Selling) continue; - auto const pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pBuilding->Type); - // The first highest priority takes precedence over lower ones - if (pBuildingTypeExt->NewEvaVoice.Get(false) && pBuildingTypeExt->NewEvaVoice_Priority > newPriority) + if (pBuildingTypeExt->NewEvaVoice_Priority > newPriority) { newPriority = pBuildingTypeExt->NewEvaVoice_Priority; newEvaIndex = pBuildingTypeExt->NewEvaVoice_Index; diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index 4da9d2bb14..016a8a55f7 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -248,9 +248,11 @@ DEFINE_HOOK(0x440B4F, BuildingClass_Unlimbo_SetShouldRebuild, 0x5) { enum { ContinueCheck = 0x440B58, SkipSetShouldRebuild = 0x440B81 }; + GET(BuildingClass* const, pThis, ESI); + auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); - if (pTypeExt->NewEvaVoice.Get(false)) + if (pTypeExt->NewEvaVoice_Index.isset()) BuildingExt::ExtMap.Find(pThis)->UpdateMainEvaVoice(); if (SessionClass::IsCampaign()) @@ -421,6 +423,11 @@ DEFINE_HOOK(0x445D87, BuildingClass_Limbo_DestroyableObstacle, 0x6) { GET(BuildingClass*, pThis, ESI); + auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + + if (pTypeExt->NewEvaVoice_Index.isset() && pTypeExt->NewEvaVoice_RecheckOnDeath) + BuildingExt::ExtMap.Find(pThis)->UpdateMainEvaVoice(); + if (BuildingTypeExt::ExtMap.Find(pThis->Type)->IsDestroyableObstacle) RecalculateCells(pThis); @@ -437,9 +444,6 @@ DEFINE_HOOK(0x445D87, BuildingClass_Limbo_DestroyableObstacle, 0x6) } } - if (pTypeExt->NewEvaVoice.Get(false) && pTypeExt->NewEvaVoice_RecheckOnDeath) - BuildingExt::ExtMap.Find(pThis)->UpdateMainEvaVoice(); - return 0; } diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index a30c6e9762..86c59a3c41 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -201,7 +201,6 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->BunkerWallsUpSound.Read(exINI, pSection, "BunkerWallsUpSound"); this->BunkerWallsDownSound.Read(exINI, pSection, "BunkerWallsDownSound"); - this->NewEvaVoice.Read(exINI, pSection, "NewEVAVoice"); this->NewEvaVoice_Index.Read(exINI, pSection, "NewEVAVoice.Index"); this->NewEvaVoice_Priority.Read(exINI, pSection, "NewEVAVoice.Priority"); this->NewEvaVoice_RecheckOnDeath.Read(exINI, pSection, "NewEVAVoice.RecheckOnDeath"); @@ -339,7 +338,6 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm) .Process(this->BuildingOccupyROFMult) .Process(this->BuildingBunkerDamageMult) .Process(this->BuildingBunkerROFMult) - .Process(this->NewEvaVoice) .Process(this->NewEvaVoice_Index) .Process(this->NewEvaVoice_Priority) .Process(this->NewEvaVoice_RecheckOnDeath) diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 119b0e857a..953b179647 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -93,8 +93,7 @@ class BuildingTypeExt NullableIdx BunkerWallsUpSound; NullableIdx BunkerWallsDownSound; - Nullable NewEvaVoice; - Valueable NewEvaVoice_Index; + Nullable NewEvaVoice_Index; Valueable NewEvaVoice_Priority; Valueable NewEvaVoice_RecheckOnDeath; NullableIdx NewEvaVoice_InitialMessage; @@ -158,8 +157,7 @@ class BuildingTypeExt , BuildingBunkerROFMult {} , BunkerWallsUpSound {} , BunkerWallsDownSound {} - , NewEvaVoice {} - , NewEvaVoice_Index { 0 } + , NewEvaVoice_Index {} , NewEvaVoice_Priority { 0 } , NewEvaVoice_RecheckOnDeath { false } , NewEvaVoice_InitialMessage { } diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 48dd6d7956..25f78bf5c1 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -316,10 +316,6 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) } } -namespace EVAIndexListTemp -{ - std::vector EVAIndexList; -} // this runs between the before and after type data loading methods for rules ini void RulesExt::ExtData::InitializeAfterTypeData(RulesClass* const pThis) { From 3d2aa77c5bf676d071d269a8db1a7696ddc54764 Mon Sep 17 00:00:00 2001 From: FS-21 Date: Tue, 8 Jul 2025 08:25:25 +0200 Subject: [PATCH 7/8] Updated docs --- docs/User-Interface.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index b5eb5f7aea..2bf67faae0 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -29,17 +29,17 @@ IngameScore.LoseTheme= ; Soundtrack theme ID ### New EVA voice after deploying a building -- You can now replace the current EVA voice when a specific structure is placed/deployed. -- `NewEVAVoice.Index` is the index of the new EVA voice. Ares is hightly recomended because these indexes are readed the new section `[EVATypes]` at `evamd.ini` created by Ares. Look at Ares documentation for more information. -- In case of multiple structures with different EVA voices `NewEVAVoice.Priority` establish a priority queue, being the highest value the selected one. +- You can now replace the current EVA voice when a specific building is placed/deployed. +- If any buiding is undeployed/sold/destroyed EVA voice will be evaluated again looking all the `NewEVAVoice.Index`. +- `NewEVAVoice.Index` is the index of the new EVA voice. Ares is hightly recomended because these indexes are reading the new section `[EVATypes]` at `evamd.ini` introduced by Ares. Look at Ares documentation regarding new EVA voices for more information. +- In case of multiple buildings with different `NewEVAVoice.Index` EVA voices then `NewEVAVoice.Priority` establish a priority queue, being the highest value the selected one. - `NewEVAVoice.RecheckOnDeath` re-checks a new EVA voice after the destruction/undeployment of of of these buildings. - `NewEVAVoice.InitialMessage` plays an EVA message to the player when a different EVA has been selected. In `rulesmd.ini`: ```ini [SOMEBUILDING] ; BuildingType -NewEVAVoice= ; boolean -NewEVAVoice.Index=0 ; integer +NewEVAVoice.Index= ; integer NewEVAVoice.Priority=1 ; integer NewEVAVoice.RecheckOnDeath=false ; boolean NewEVAVoice.InitialMessage= ; EVA entry From 4e1443e6ca8709a70f7bb5b0b3a3237d746dc79d Mon Sep 17 00:00:00 2001 From: FS-21 Date: Tue, 8 Jul 2025 10:45:02 +0200 Subject: [PATCH 8/8] Applied feedback Function UpdateMainEvaVoice(...) moved from Buildings to side class. --- src/Ext/Building/Body.cpp | 84 ------------------------------------- src/Ext/Building/Body.h | 1 - src/Ext/Building/Hooks.cpp | 11 ++--- src/Ext/Rules/Body.cpp | 2 +- src/Ext/Side/Body.cpp | 86 ++++++++++++++++++++++++++++++++++++++ src/Ext/Side/Body.h | 3 ++ 6 files changed, 94 insertions(+), 93 deletions(-) diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index b776d919f9..493c64b433 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -449,90 +449,6 @@ const std::vector BuildingExt::GetFoundationCells(BuildingClass* con return foundationCells; } -void BuildingExt::ExtData::UpdateMainEvaVoice() -{ - const auto pTypeExt = this->TypeExtData; - - if (!pTypeExt->NewEvaVoice_Index.isset()) - return; - - const auto pThis = this->OwnerObject(); - const auto pHouse = pThis->Owner; - - if (!pHouse->IsControlledByCurrentPlayer()) - return; - - int newPriority = -1; - int newEvaIndex = VoxClass::EVAIndex; - - for (const auto pBuilding : pHouse->Buildings) - { - const auto pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pBuilding->Type); - - // Special case that must be avoided here because can be the current EVA changer - if (!pBuildingTypeExt->NewEvaVoice_Index.isset() || pBuilding->CurrentMission == Mission::Selling) - continue; - - // The first highest priority takes precedence over lower ones - if (pBuildingTypeExt->NewEvaVoice_Priority > newPriority) - { - newPriority = pBuildingTypeExt->NewEvaVoice_Priority; - newEvaIndex = pBuildingTypeExt->NewEvaVoice_Index; - } - } - - if (pThis->CurrentMission != Mission::Selling && pTypeExt->NewEvaVoice_Priority > newPriority) - { - newPriority = pTypeExt->NewEvaVoice_Priority; - newEvaIndex = pTypeExt->NewEvaVoice_Index; - } - - if (newPriority > 0 && VoxClass::EVAIndex != newEvaIndex) - { - VoxClass::EVAIndex = newEvaIndex; - - // Greeting of the new EVA voice - int idxPlay = pTypeExt->NewEvaVoice_InitialMessage.Get(-1); - - if (idxPlay != -1) - VoxClass::PlayIndex(idxPlay); - } - else if (newPriority < 0) - { - // Restore the original EVA voice of the owner's side - const auto pSide = SideClass::Array.GetItem(pHouse->SideIndex); - const auto pSideExt = SideExt::ExtMap.Find(pSide); - newEvaIndex = 0; // By default is set the "Allies" EVA voice - - if (pSideExt->EVA_Tag.isset()) - { - const auto evaTag = pSideExt->EVA_Tag.Get(); - - for (size_t i = 0; i < RulesExt::Global()->EVAIndexList.size(); i++) - { - const auto item = RulesExt::Global()->EVAIndexList[i].c_str(); - - if (_strcmpi(item, evaTag) == 0) - { - newEvaIndex = i; - break; - } - } - } - else - { - if (pHouse->SideIndex == 0) - newEvaIndex = 0; // Allied - if (pHouse->SideIndex == 1) - newEvaIndex = 1; // Russian - else if (pHouse->SideIndex == 2) - newEvaIndex = 2; // Yuri - } - - VoxClass::EVAIndex = newEvaIndex; - } -} - // ============================= // load / save diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index e9620df2c1..6d0d562907 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -59,7 +59,6 @@ class BuildingExt bool HasSuperWeapon(int index, bool withUpgrades) const; bool HandleInfiltrate(HouseClass* pInfiltratorHouse, int moneybefore); void UpdatePrimaryFactoryAI(); - void UpdateMainEvaVoice(); virtual ~ExtData() = default; // virtual void LoadFromINIFile(CCINIClass* pINI) override; diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index 806b7c25b6..bb8422b1cb 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -253,15 +254,11 @@ DEFINE_HOOK(0x440B4F, BuildingClass_Unlimbo_SetShouldRebuild, 0x5) GET(BuildingClass* const, pThis, ESI); - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); - - if (pTypeExt->NewEvaVoice_Index.isset()) - BuildingExt::ExtMap.Find(pThis)->UpdateMainEvaVoice(); + if (BuildingTypeExt::ExtMap.Find(pThis->Type)->NewEvaVoice_Index.isset()) + SideExt::UpdateMainEvaVoice(pThis); if (SessionClass::IsCampaign()) { - GET(BuildingClass* const, pThis, ESI); - // Preplaced structures are already managed before if (BuildingExt::ExtMap.Find(pThis)->IsCreatedFromMapFile) return SkipSetShouldRebuild; @@ -429,7 +426,7 @@ DEFINE_HOOK(0x445D87, BuildingClass_Limbo_DestroyableObstacle, 0x6) auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); if (pTypeExt->NewEvaVoice_Index.isset() && pTypeExt->NewEvaVoice_RecheckOnDeath) - BuildingExt::ExtMap.Find(pThis)->UpdateMainEvaVoice(); + SideExt::UpdateMainEvaVoice(pThis); if (BuildingTypeExt::ExtMap.Find(pThis->Type)->IsDestroyableObstacle) RecalculateCells(pThis); diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 2f80329c11..001f62ecc2 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -593,7 +593,7 @@ void RulesExt::ExtData::LoadEvaVoices() this->EVAIndexList.emplace_back(GameStrings::Yuri); // New EVA voices due to a new Ares section in evamd.ini - const auto count = iniEva.GetKeyCount(pEvaSection); + const auto count = (std::size_t)iniEva.GetKeyCount(pEvaSection); for (std::size_t i = 0; i < count; i++) { diff --git a/src/Ext/Side/Body.cpp b/src/Ext/Side/Body.cpp index 1ecc0f35ee..845517c7f5 100644 --- a/src/Ext/Side/Body.cpp +++ b/src/Ext/Side/Body.cpp @@ -2,6 +2,9 @@ #include +#include +#include + SideExt::ExtContainer SideExt::ExtMap; void SideExt::ExtData::Initialize() @@ -52,6 +55,89 @@ void SideExt::ExtData::LoadFromINIFile(CCINIClass* pINI) this->EVA_Tag = _strdup(Phobos::readBuffer); } +void SideExt::UpdateMainEvaVoice(BuildingClass* pThis) +{ + const auto pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + + if (!pTypeExt->NewEvaVoice_Index.isset()) + return; + + const auto pHouse = pThis->Owner; + + if (!pHouse->IsControlledByCurrentPlayer()) + return; + + int newPriority = -1; + int newEvaIndex = VoxClass::EVAIndex; + + for (const auto pBuilding : pHouse->Buildings) + { + const auto pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pBuilding->Type); + + // Special case that must be avoided here because can be the current EVA changer + if (!pBuildingTypeExt->NewEvaVoice_Index.isset() || pBuilding->CurrentMission == Mission::Selling) + continue; + + // The first highest priority takes precedence over lower ones + if (pBuildingTypeExt->NewEvaVoice_Priority > newPriority) + { + newPriority = pBuildingTypeExt->NewEvaVoice_Priority; + newEvaIndex = pBuildingTypeExt->NewEvaVoice_Index; + } + } + + if (pThis->CurrentMission != Mission::Selling && pTypeExt->NewEvaVoice_Priority > newPriority) + { + newPriority = pTypeExt->NewEvaVoice_Priority; + newEvaIndex = pTypeExt->NewEvaVoice_Index; + } + + if (newPriority > 0 && VoxClass::EVAIndex != newEvaIndex) + { + VoxClass::EVAIndex = newEvaIndex; + + // Greeting of the new EVA voice + int idxPlay = pTypeExt->NewEvaVoice_InitialMessage.Get(-1); + + if (idxPlay != -1) + VoxClass::PlayIndex(idxPlay); + } + else if (newPriority < 0) + { + // Restore the original EVA voice of the owner's side + const auto pSide = SideClass::Array.GetItem(pHouse->SideIndex); + const auto pSideExt = SideExt::ExtMap.Find(pSide); + newEvaIndex = 0; // By default is set the "Allies" EVA voice + + if (pSideExt->EVA_Tag.isset()) + { + const auto evaTag = pSideExt->EVA_Tag.Get(); + + for (size_t i = 0; i < RulesExt::Global()->EVAIndexList.size(); i++) + { + const auto item = RulesExt::Global()->EVAIndexList[i].c_str(); + + if (_strcmpi(item, evaTag) == 0) + { + newEvaIndex = i; + break; + } + } + } + else + { + if (pHouse->SideIndex == 0) + newEvaIndex = 0; // Allied + if (pHouse->SideIndex == 1) + newEvaIndex = 1; // Russian + else if (pHouse->SideIndex == 2) + newEvaIndex = 2; // Yuri + } + + VoxClass::EVAIndex = newEvaIndex; + } +} + // ============================= // load / save diff --git a/src/Ext/Side/Body.h b/src/Ext/Side/Body.h index a69c1a84e0..35eb40f1b3 100644 --- a/src/Ext/Side/Body.h +++ b/src/Ext/Side/Body.h @@ -94,6 +94,9 @@ class SideExt }; static ExtContainer ExtMap; + static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); + + static void UpdateMainEvaVoice(BuildingClass* pThis); };