Skip to content
Draft
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
2 changes: 2 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,7 @@ This page lists all the individual contributions to the project by their author.
- Return warhead
- `ElectricAssault` weapons can now auto acquire allies' overpowerable defenses
- Allow `AuxBuilding` and Ares' `SW.Aux/NegBuildings` to count building upgrades
- Dynamic music framework
- **NaotoYuuki** - Vertical & meteor trajectory projectile prototypes
- **handama**:
- AI script action to `16005 Jump Back To Previous Script`
Expand Down Expand Up @@ -812,6 +813,7 @@ This page lists all the individual contributions to the project by their author.
- **Damfoos** - extensive and thorough testing
- **Dmitry Volkov** - extensive and thorough testing
- **Rise of the East community** - extensive playtesting of in-dev features
- **ahasasjeb** - Add music to super weapons
- **11EJDE11** - Prevent mpdebug number from being drawn when visibility toggled off
- **RAZER**:
- Wall overlay unit sell exploit fix
Expand Down
40 changes: 40 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1310,6 +1310,26 @@ Detonate.Damage= ; integer
Detonate.AtFirer=false ; boolean
```

### Superweapon music control

- Superweapons can now play a theme when fired and optionally stop after a configurable duration. This will replace any theme that's being played now.
- `Music.Theme` selects the soundtrack theme by its ID defined in `thememd.ini`.
- `Music.Duration` controls how many frames this theme will last. Set it to below 0 for an infinite duration. Player can still switch music manually no matter if it lasts infinitely.
- When the timer is completed and `Music.Theme` is still being played, it'll be stopped and switched to default music list. If another music is already played, it won't be switched.
- `Music.AffectsHouse` determines which houses will be affected by the music change of this superweapon.

In `rulesmd.ini`:
```ini
[SOMESW] ; SuperWeaponType
Music.Theme=-1 ; Theme ID
Music.Duration=0 ; integer, game frames
Music.AffectsHouse=all ; Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all)
```

```{note}
To loop the music correctly during this period, set `Repeat=yes` for the corresponding theme in `thememd.ini`. Otherwise, the track may stop at its end even if `Music.Duration` has not elapsed.
```

## Technos

### Aggressive attack move mission
Expand Down Expand Up @@ -1543,6 +1563,26 @@ BuildLimitGroup.ExtraLimit.MaxCount= ; List of integers
BuildLimitGroup.ExtraLimit.MaxNum=0 ; integer
```

### Combat music

- Music can now be changed when under attack by enemies or damaging an enemy objects.
- `CombatMusic.Theme` selects the soundtrack theme by its ID defined in `thememd.ini`.
- `CombatMusic.Duration` controls how many frames this theme will last. Set it to below 0 for an infinite duration. Player can still switch music manually no matter if it lasts infinitely.
- When the timer is completed and `CombatMusic.Theme` is still being played, it'll be stopped and switched to default music list. If another music is already played, it won't be switched.
- `CombatMusic.UnderAttack` determines if the combat music is triggered by an attack or under attack.

In `rulesmd.ini`:
```ini
[SOMESIDE] ; Side
CombatMusic.Theme=-1 ; Theme ID
CombatMusic.Duration=0 ; integer, game frames
CombatMusic.UnderAttack=true ; boolean
```

```{note}
To loop the music correctly during this period, set `Repeat=yes` for the corresponding theme in `thememd.ini`. Otherwise, the track may stop at its end even if `Music.Duration` has not elapsed.
```

### Convert TechnoType on owner house change

- You can now change a unit's type when changing ownership from human to computer or from computer to human.
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ HideShakeEffects=false ; boolean
- Allow `RemoveMindControl` warhead to mute `MindClearedSound` (by Noble_Fish)
- Introduce weight selection rules for ExtraWarheads (by Noble_Fish)
- [Allow infantry to perform type conversion when deploying and undeploying](New-or-Enhanced-Logics.md#allow-infantry-to-perform-type-conversion-when-deploying-and-undeploying) (by Noble_Fish)
- Dynamic music system (by Ollerus and ahasasjeb)

#### Vanilla fixes:
- Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya)
Expand Down
30 changes: 30 additions & 0 deletions src/Ext/House/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,34 @@ void HouseExt::ExtData::SetForceEnemyIndex(int EnemyIndex)
this->ForceEnemyIndex = EnemyIndex;
}

void HouseExt::ExtData::MusicChange(int music, int duration)
{
// restrict to current house
if (this->OwnerObject() != HouseClass::CurrentPlayer)
return;

if (music >= 0)
{
ThemeClass::Instance.Play(music);
this->MusicTheme = music;

if (duration > 0)
this->MusicDuration.Start(duration);
}
}

void HouseExt::ExtData::MusicStop()
{
// restrict to current house
if (this->OwnerObject() != HouseClass::CurrentPlayer)
return;

if (ThemeClass::Instance.CurrentTheme == this->MusicTheme)
ThemeClass::Instance.Stop();

this->MusicTheme = -1;
}

void HouseExt::CalculatePowerSurplus(HouseClass* pThis)
{
auto const pRulesExt = RulesExt::Global();
Expand Down Expand Up @@ -712,6 +740,8 @@ void HouseExt::ExtData::Serialize(T& Stm)
.Process(this->FreeRadar)
.Process(this->ForceRadar)
.Process(this->PlayerAutoRepair)
.Process(this->MusicDuration)
.Process(this->MusicTheme)
;
}

Expand Down
8 changes: 8 additions & 0 deletions src/Ext/House/Body.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include <HouseClass.h>
#include <ThemeClass.h>

#include <Utilities/Container.h>
#include <Utilities/TemplateDef.h>
Expand Down Expand Up @@ -69,6 +70,9 @@ class HouseExt

bool PlayerAutoRepair;

CDTimerClass MusicDuration;
int MusicTheme;

ExtData(HouseClass* OwnerObject) : Extension<HouseClass>(OwnerObject)
, PowerPlantEnhancers {}
, OwnedLimboDeliveredBuildings {}
Expand Down Expand Up @@ -103,6 +107,8 @@ class HouseExt
, FreeRadar(false)
, ForceRadar(false)
, PlayerAutoRepair(true)
, MusicDuration {}
, MusicTheme { -1 }
{ }

bool OwnsLimboDeliveredBuilding(BuildingClass* pBuilding) const;
Expand All @@ -115,6 +121,8 @@ class HouseExt

int GetForceEnemyIndex();
void SetForceEnemyIndex(int EnemyIndex);
void MusicChange(int music, int duration);
void MusicStop();

virtual ~ExtData() = default;

Expand Down
6 changes: 4 additions & 2 deletions src/Ext/House/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,11 +379,10 @@ DEFINE_HOOK(0x4F9038, HouseClass_AI_Superweapons, 0x5)
return 0;

const int delay = RulesExt::Global()->AISuperWeaponDelay.Get();
auto const pExt = HouseExt::ExtMap.Find(pThis);

if (delay > 0)
{
auto const pExt = HouseExt::ExtMap.Find(pThis);

if (pExt->AISuperWeaponDelayTimer.HasTimeLeft())
return 0;

Expand All @@ -393,6 +392,9 @@ DEFINE_HOOK(0x4F9038, HouseClass_AI_Superweapons, 0x5)
if (!SessionClass::IsCampaign() || pThis->IQLevel2 >= RulesClass::Instance->SuperWeapons)
pThis->AI_TryFireSW();

if (pExt->MusicDuration.Completed())
pExt->MusicStop();

return 0;
}

Expand Down
7 changes: 7 additions & 0 deletions src/Ext/SWType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ void SWTypeExt::ExtData::Serialize(T& Stm)
.Process(this->SW_Link_RollChances)
.Process(this->Message_LinkedSWAcquired)
.Process(this->EVA_LinkedSWAcquired)
.Process(this->Music_Theme)
.Process(this->Music_Duration)
.Process(this->Music_AffectsHouse)
;
}

Expand Down Expand Up @@ -293,6 +296,10 @@ void SWTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
pNewSWType->Initialize(const_cast<SWTypeExt::ExtData*>(this), OwnerObject());
pNewSWType->LoadFromINI(const_cast<SWTypeExt::ExtData*>(this), OwnerObject(), pINI);
}

this->Music_Theme = pINI->ReadTheme(pSection, "Music.Theme", this->Music_Theme);
this->Music_Duration.Read(exINI, pSection, "Music.Duration");
this->Music_AffectsHouse.Read(exINI, pSection, "Music.AffectsHouse");
}

void SWTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm)
Expand Down
7 changes: 7 additions & 0 deletions src/Ext/SWType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ class SWTypeExt
Valueable<CSFText> Message_LinkedSWAcquired;
NullableIdx<VoxClass> EVA_LinkedSWAcquired;

Valueable<int> Music_Theme;
Valueable<int> Music_Duration;
Valueable<AffectedHouse> Music_AffectsHouse;

ExtData(SuperWeaponTypeClass* OwnerObject) : Extension<SuperWeaponTypeClass>(OwnerObject)
, TypeID { "" }
, Money_Amount { 0 }
Expand Down Expand Up @@ -195,6 +199,9 @@ class SWTypeExt
, SW_Link_RandomWeightsData {}
, Message_LinkedSWAcquired {}
, EVA_LinkedSWAcquired {}
, Music_Theme { -1 }
, Music_Duration { 0 }
, Music_AffectsHouse { AffectedHouse::All }
{ }

// Ares 0.A functions
Expand Down
3 changes: 3 additions & 0 deletions src/Ext/SWType/FireSuperWeapon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ void SWTypeExt::FireSuperWeaponExt(SuperClass* pSW, const CellStruct& cell)
auto& sw_ext = HouseExt::ExtMap.Find(pHouse)->SuperExts[pType->ArrayIndex];
sw_ext.ShotCount++;

if (EnumFunctions::CanTargetHouse(pTypeExt->Music_AffectsHouse, pHouse, HouseClass::CurrentPlayer))
HouseExt::ExtMap.Find(HouseClass::CurrentPlayer)->MusicChange(pTypeExt->Music_Theme, pTypeExt->Music_Duration);

const auto pTags = &pHouse->RelatedTags;

if (pTags->Count > 0)
Expand Down
6 changes: 6 additions & 0 deletions src/Ext/Side/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ 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");
this->CombatMusic_Theme = pINI->ReadTheme(pSection, "CombatMusic.Theme", this->CombatMusic_Theme);
this->CombatMusic_Duration.Read(exINI, pSection, "CombatMusic.Duration");
this->CombatMusic_UnderAttack.Read(exINI, pSection, "CombatMusic.UnderAttack");
}

// =============================
Expand Down Expand Up @@ -93,6 +96,9 @@ void SideExt::ExtData::Serialize(T& Stm)
.Process(this->SuperWeaponSidebar_TopPCX)
.Process(this->SuperWeaponSidebar_CenterPCX)
.Process(this->SuperWeaponSidebar_BottomPCX)
.Process(this->CombatMusic_Theme)
.Process(this->CombatMusic_Duration)
.Process(this->CombatMusic_UnderAttack)
;
}

Expand Down
6 changes: 6 additions & 0 deletions src/Ext/Side/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class SideExt
PhobosPCXFile SuperWeaponSidebar_TopPCX;
PhobosPCXFile SuperWeaponSidebar_CenterPCX;
PhobosPCXFile SuperWeaponSidebar_BottomPCX;
Valueable<int> CombatMusic_Theme;
Valueable<int> CombatMusic_Duration;
Valueable<bool> CombatMusic_UnderAttack;

ExtData(SideClass* OwnerObject) : Extension<SideClass>(OwnerObject)
, ArrayIndex { -1 }
Expand Down Expand Up @@ -75,6 +78,9 @@ class SideExt
, SuperWeaponSidebar_TopPCX {}
, SuperWeaponSidebar_CenterPCX {}
, SuperWeaponSidebar_BottomPCX {}
, CombatMusic_Theme { -1 }
, CombatMusic_Duration { 0 }
, CombatMusic_UnderAttack { true }
{ }

virtual ~ExtData() = default;
Expand Down
18 changes: 16 additions & 2 deletions src/Ext/Techno/Hooks.ReceiveDamage.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "Body.h"

#include <Ext/House/Body.h>
#include <Ext/Side/Body.h>
#include <Ext/TEvent/Body.h>
#include <Ext/WarheadType/Body.h>
#include <Ext/WeaponType/Body.h>
Expand Down Expand Up @@ -75,15 +76,15 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6)
}

// Raise Combat Alert
const auto pHouseExt = HouseExt::ExtMap.Find(pTargetHouse);

if (RulesExt::Global()->CombatAlert && damage > 1)
{
auto raiseCombatAlert = [&]()
{
if (!pTargetHouse->IsControlledByCurrentPlayer() || (RulesExt::Global()->CombatAlert_SuppressIfAllyDamage && pTargetHouse->IsAlliedWith(pSourceHouse)))
return;

const auto pHouseExt = HouseExt::ExtMap.Find(pTargetHouse);

if (pHouseExt->CombatAlertTimer.HasTimeLeft() || pWHExt->CombatAlert_Suppress.Get(!pWHExt->Malicious || pWHExt->Nonprovocative))
return;

Expand Down Expand Up @@ -129,6 +130,19 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6)
raiseCombatAlert();
}

// Combat Music
if (const auto pTargetSideExt = SideExt::ExtMap.TryFind(SideClass::Array.GetItemOrDefault(pTargetHouse->SideIndex)))
{
if (pTargetSideExt->CombatMusic_UnderAttack && !pTargetHouse->IsAlliedWith(pSourceHouse))
pHouseExt->MusicChange(pTargetSideExt->CombatMusic_Theme, pTargetSideExt->CombatMusic_Duration);
}

if (const auto pSourceSideExt = SideExt::ExtMap.TryFind(SideClass::Array.GetItemOrDefault(pSourceHouse->SideIndex)))
{
if (!pSourceSideExt->CombatMusic_UnderAttack && !pSourceHouse->IsAlliedWith(pTargetHouse) && !pTargetHouse->IsNeutral())
HouseExt::ExtMap.Find(pSourceHouse)->MusicChange(pSourceSideExt->CombatMusic_Theme, pSourceSideExt->CombatMusic_Duration);
}

// Shield Receive Damage
if (!args->IgnoreDefenses)
{
Expand Down
Loading