Skip to content

New EVA voice after deploying a building #1682

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,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 EVA voice after deploying a building
- **Starkku**:
- Misc. minor bugfixes & improvements
- AI script actions:
Expand Down
2 changes: 1 addition & 1 deletion YRpp
Submodule YRpp updated 1 files
+3 −3 ScenarioClass.h
18 changes: 18 additions & 0 deletions docs/User-Interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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.Index= ; integer
NewEVAVoice.Priority=1 ; integer
NewEVAVoice.RecheckOnDeath=false ; boolean
NewEVAVoice.InitialMessage= ; EVA entry
```

## Battle screen UI/UX

### Digital display
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ New:
- [Damaged aircraft image changes](New-or-Enhanced-Logics.md#damaged-aircraft-image-changes) (by Fryone)
- [Additional attached animation position customizations](Fixed-or-Improved-Logics.md#attached-animation-position-customization) (by Starkku)
- Use `SkipCrushSlowdown=true` to avoid the bug related to `Accelerates=true` and `MovementZone=CrushAll` (by TaranDahl)
- 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)
Expand Down
3 changes: 3 additions & 0 deletions src/Ext/Building/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#include <BitFont.h>

#include <Utilities/EnumFunctions.h>
#include <Ext/Side/Body.h>
#include <Ext/Scenario/Body.h>
#include <Ext/House/Body.h>

BuildingExt::ExtContainer BuildingExt::ExtMap;

Expand Down
13 changes: 11 additions & 2 deletions src/Ext/Building/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <Ext/House/Body.h>
#include <Ext/SWType/Body.h>
#include <Ext/WarheadType/Body.h>
#include <Ext/Side/Body.h>
#include <TacticalClass.h>
#include <PlanningTokenClass.h>

Expand Down Expand Up @@ -257,10 +258,13 @@ DEFINE_HOOK(0x440B4F, BuildingClass_Unlimbo_SetShouldRebuild, 0x5)
{
enum { ContinueCheck = 0x440B58, SkipSetShouldRebuild = 0x440B81 };

GET(BuildingClass* const, pThis, ESI);

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;
Expand Down Expand Up @@ -425,6 +429,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)
SideExt::UpdateMainEvaVoice(pThis);

if (BuildingTypeExt::ExtMap.Find(pThis->Type)->IsDestroyableObstacle)
RecalculateCells<true>(pThis);

Expand Down
9 changes: 9 additions & 0 deletions src/Ext/BuildingType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->BunkerWallsDownSound.Read(exINI, pSection, "BunkerWallsDownSound");
this->BuildingRepairedSound.Read(exINI, pSection, "BuildingRepairedSound");

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();
Expand Down Expand Up @@ -334,6 +339,10 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm)
.Process(this->BuildingRepairedSound)
.Process(this->Refinery_UseNormalActiveAnim)
.Process(this->HasPowerUpAnim)
.Process(this->NewEvaVoice_Index)
.Process(this->NewEvaVoice_Priority)
.Process(this->NewEvaVoice_RecheckOnDeath)
.Process(this->NewEvaVoice_InitialMessage)
;
}

Expand Down
9 changes: 9 additions & 0 deletions src/Ext/BuildingType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ class BuildingTypeExt

ValueableVector<bool> HasPowerUpAnim;

Nullable<int> NewEvaVoice_Index;
Valueable<int> NewEvaVoice_Priority;
Valueable<bool> NewEvaVoice_RecheckOnDeath;
NullableIdx<VoxClass> NewEvaVoice_InitialMessage;

ExtData(BuildingTypeClass* OwnerObject) : Extension<BuildingTypeClass>(OwnerObject)
, PowersUp_Owner { AffectedHouse::Owner }
, PowersUp_Buildings {}
Expand Down Expand Up @@ -161,6 +166,10 @@ class BuildingTypeExt
, BuildingRepairedSound {}
, Refinery_UseNormalActiveAnim { false }
, HasPowerUpAnim {}
, NewEvaVoice_Index {}
, NewEvaVoice_Priority { 0 }
, NewEvaVoice_RecheckOnDeath { false }
, NewEvaVoice_InitialMessage { }
{ }

// Ares 0.A functions
Expand Down
47 changes: 47 additions & 0 deletions src/Ext/Rules/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,9 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI)
this->DistributeTargetingFrame.Read(exINI, GameStrings::General, "DistributeTargetingFrame");
this->DistributeTargetingFrame_AIOnly.Read(exINI, GameStrings::General, "DistributeTargetingFrame.AIOnly");

// Reading Ares section [EVATypes] at evamd.ini
LoadEvaVoices();

// Section AITargetTypes
int itemsCount = pINI->GetKeyCount("AITargetTypes");
for (int i = 0; i < itemsCount; ++i)
Expand Down Expand Up @@ -548,6 +551,7 @@ void RulesExt::ExtData::Serialize(T& Stm)
.Process(this->TintColorBerserk)
.Process(this->AttackMove_IgnoreWeaponCheck)
.Process(this->AttackMove_StopWhenTargetAcquired)
.Process(this->EVAIndexList)
;
}

Expand Down Expand Up @@ -590,6 +594,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 = (std::size_t)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

Expand Down
5 changes: 5 additions & 0 deletions src/Ext/Rules/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ class RulesExt
int TintColorForceShield;
int TintColorBerserk;

std::vector<std::string> EVAIndexList;

ExtData(RulesClass* OwnerObject) : Extension<RulesClass>(OwnerObject)
, Storage_TiberiumIndex { -1 }
, HarvesterDumpAmount { 0.0f }
Expand Down Expand Up @@ -443,6 +445,8 @@ class RulesExt

, AttackMove_IgnoreWeaponCheck { false }
, AttackMove_StopWhenTargetAcquired { }

, EVAIndexList {}
{ }

virtual ~ExtData() = default;
Expand All @@ -460,6 +464,7 @@ class RulesExt
virtual void SaveToStream(PhobosStreamWriter& Stm) override;

void ReplaceVoxelLightSources();
void LoadEvaVoices();

private:
template <typename T>
Expand Down
92 changes: 92 additions & 0 deletions src/Ext/Side/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

#include <ThemeClass.h>

#include <Ext/Building/Body.h>
#include <Ext/BuildingType/Body.h>

SideExt::ExtContainer SideExt::ExtMap;

void SideExt::ExtData::Initialize()
Expand Down Expand Up @@ -45,6 +48,94 @@ void SideExt::ExtData::LoadFromINIFile(CCINIClass* pINI)
this->SuperWeaponSidebar_TopPCX.Read(pINI, pSection, "SuperWeaponSidebar.TopPCX");
this->SuperWeaponSidebar_CenterPCX.Read(pINI, pSection, "SuperWeaponSidebar.CenterPCX");
this->SuperWeaponSidebar_BottomPCX.Read(pINI, pSection, "SuperWeaponSidebar.BottomPCX");

pINI->ReadString(pSection, "EVA.Tag", "", Phobos::readBuffer);

if (std::strlen(Phobos::readBuffer) > 0)
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;
}
}

// =============================
Expand Down Expand Up @@ -79,6 +170,7 @@ void SideExt::ExtData::Serialize(T& Stm)
.Process(this->SuperWeaponSidebar_TopPCX)
.Process(this->SuperWeaponSidebar_CenterPCX)
.Process(this->SuperWeaponSidebar_BottomPCX)
.Process(this->EVA_Tag)
;
}

Expand Down
5 changes: 5 additions & 0 deletions src/Ext/Side/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class SideExt
PhobosPCXFile SuperWeaponSidebar_TopPCX;
PhobosPCXFile SuperWeaponSidebar_CenterPCX;
PhobosPCXFile SuperWeaponSidebar_BottomPCX;
Nullable<char*> EVA_Tag;

ExtData(SideClass* OwnerObject) : Extension<SideClass>(OwnerObject)
, ArrayIndex { -1 }
Expand Down Expand Up @@ -68,6 +69,7 @@ class SideExt
, SuperWeaponSidebar_TopPCX {}
, SuperWeaponSidebar_CenterPCX {}
, SuperWeaponSidebar_BottomPCX {}
, EVA_Tag { }
{ }

virtual ~ExtData() = default;
Expand All @@ -92,6 +94,9 @@ class SideExt
};

static ExtContainer ExtMap;

static bool LoadGlobals(PhobosStreamReader& Stm);
static bool SaveGlobals(PhobosStreamWriter& Stm);

static void UpdateMainEvaVoice(BuildingClass* pThis);
};
Loading