diff --git a/CREDITS.md b/CREDITS.md index d6903a4ba..3b807829f 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -345,6 +345,8 @@ This page lists all the individual contributions to the project by their author. - `TurretOffset` support for SHP vehicles - Customizable rocker amplitude - Customizable wake anim + - Customizable spawns queue + - Initial spawns number - **Fryone** - Customizable ElectricBolt Arcs - Sound entry on unit's creation diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 6eb539bd4..febf83835 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -1012,6 +1012,18 @@ OpenTopped.AllowFiringIfDeactivated=true ; boolean OpenTopped.ShareTransportTarget=true ; boolean ``` +### Customizable spawns queue +- It is now possible to spawn multiple types of spawnees from a spawner with `Spawns.Queue`. The order of spawnees in this queue is the order of their respawn. + - `Spawns` still needs to be set to enable the spawner logic and act as a default spawnee. + - `SpawnsNumber` still needs to be set to determine the amount of spawnee slots. + - If the length of the queue is longer than the spawner's `SpawnsNumber`, spawnee after this length will be omitted. If the length is shorter however, the rest of the positions will be filled by the spawnee defined by `Spawns`. Hence, it's recommended to make them the same. + +In `rulesmd.ini`: +```ini +[SOMETECHNO] ; TechnoType +Spawns.Queue= ; list of aircrafts in order +``` + ### Disabling fallback to (Elite)Secondary weapon - It is now possible to disable the fallback to `(Elite)Secondary` weapon from `(Elite)Primary` weapon if it cannot fire at the chosen target by setting `NoSecondaryWeaponFallback` to true (defaults to false). `NoSecondaryWeaponFallback.AllowAA` controls whether or not fallback because of projectile `AA` setting and target being in air is still allowed. This does not apply to special cases where `(Elite)Secondary` weapon is always chosen, including but not necessarily limited to the following: @@ -1086,6 +1098,15 @@ OmniFire=yes OmniFire.TurnToTarget=no ; boolean ``` +### Initial spawns number +- It is now possible to set the initial amount of spawnees for a spawner, instead of always being filled. Won't work if it's larger than `SpawnsNumber`. + +In `rulesmd.ini`: +```ini +[SOMETECHNO] ; TechnoType +InitialSpawnsNumber= ; integer +``` + ### Initial strength for TechnoTypes and cloned infantry ![image](_static/images/initialstrength.cloning-01.png) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 96ea81b0e..a5d0b92a2 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -453,6 +453,8 @@ New: - Option to restore `PowerSurplus` setting for AI (by Starkku) - `FireOnce` infantry sequence reset toggle (by Starkku) - Assign Super Weapon cameo to any sidebar tab (by NetsuNegi) +- Customizable spawns queue (by TwinkleStar) +- Initial spawns number (by TwinkleStar) Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) diff --git a/src/Ext/Techno/Hooks.Misc.cpp b/src/Ext/Techno/Hooks.Misc.cpp index 9449af334..de95c42a2 100644 --- a/src/Ext/Techno/Hooks.Misc.cpp +++ b/src/Ext/Techno/Hooks.Misc.cpp @@ -98,3 +98,56 @@ DEFINE_HOOK(0x6B7600, SpawnManagerClass_AI_InitDestination, 0x6) return R->Origin() == 0x6B7600 ? SkipGameCode1 : SkipGameCode2; } + +DEFINE_HOOK(0x6B6D44, SpawnManagerClass_Init_Spawns, 0x5) +{ + enum { Jump = 0x6B6DF0, Change = 0x6B6D53, Continue = 0 }; + GET(SpawnManagerClass*, pThis, ESI); + GET_STACK(size_t, i, STACK_OFFSET(0x1C, 0x4)); + + auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Owner->GetTechnoType()); + + if ((int) i >= pTypeExt->InitialSpawnsNumber.Get(pThis->SpawnCount)) + { + GET(SpawnControl*, pControl, EBP); + pControl->Unit = nullptr; + pControl->SpawnTimer.Start(pThis->RegenRate); + pControl->Status = SpawnNodeStatus::Dead; + pThis->SpawnedNodes.AddItem(pControl); + return Jump; + } + + if (pTypeExt->Spawns_Queue.size() <= i || !pTypeExt->Spawns_Queue[i]) + return Continue; + + R->EAX(pTypeExt->Spawns_Queue[i]->CreateObject(pThis->Owner->GetOwningHouse())); + return Change; +} + +DEFINE_HOOK(0x6B78D3, SpawnManagerClass_Update_Spawns, 0x6) +{ + GET(SpawnManagerClass*, pThis, ESI); + + auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Owner->GetTechnoType()); + + if (pTypeExt->Spawns_Queue.empty()) + return 0; + + std::vector vec = pTypeExt->Spawns_Queue; + + for (auto pNode : pThis->SpawnedNodes) + { + if (pNode->Unit) + { + auto it = std::find_if(vec.begin(), vec.end(), [=](auto pType) { return pType == pNode->Unit->GetTechnoType(); }); + if (it != vec.end()) + vec.erase(it); + } + } + + if (vec.empty() || !vec[0]) + return 0; + + R->EAX(vec[0]->CreateObject(pThis->Owner->GetOwningHouse())); + return 0x6B78EA; +} diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index aef8425b0..939827c10 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -318,6 +318,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Wake.Read(exINI, pSection, "Wake"); this->Wake_Grapple.Read(exINI, pSection, "Wake.Grapple"); this->Wake_Sinking.Read(exINI, pSection, "Wake.Sinking"); + this->InitialSpawnsNumber.Read(exINI, pSection, "InitialSpawnsNumber"); + this->Spawns_Queue.Read(exINI, pSection, "Spawns.Queue"); // Ares 0.2 this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius"); @@ -688,6 +690,9 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->Wake) .Process(this->Wake_Grapple) .Process(this->Wake_Sinking) + + .Process(this->InitialSpawnsNumber) + .Process(this->Spawns_Queue) ; } void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index b39018854..dc9b64606 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -233,6 +233,9 @@ class TechnoTypeExt Nullable Wake_Grapple; Nullable Wake_Sinking; + Nullable InitialSpawnsNumber; + ValueableVector Spawns_Queue; + struct LaserTrailDataEntry { ValueableIdx idxType; @@ -461,6 +464,9 @@ class TechnoTypeExt , Wake { } , Wake_Grapple { } , Wake_Sinking { } + + , InitialSpawnsNumber { } + , Spawns_Queue { } { } virtual ~ExtData() = default;