Skip to content

Commit 8bd666c

Browse files
authored
[Vanilla Enhancement] DebrisMinimums (#1734)
- Now, the original `DebrisMaximums` can be used in conjunction with new `DebrisMinimums` to limit the quantity of `DebrisTypes` when `DebrisTypes.Limit` is enabled. - The default value of `DebrisTypes.Limit` is whether the number of `DebrisMaximums` is greater than (not equal to) 1 (for compatibility reasons). In `rulesmd.ini`: ```ini [SOMETECHNO] ; TechnoType DebrisTypes.Limit= ; boolean DebrisMaximums= ; List of integers DebrisMinimums= ; List of integers [SOMEWARHEAD] ; WarheadType DebrisTypes.Limit= ; boolean DebrisMaximums= ; List of integers DebrisMinimums= ; List of integers ``` ```{hint} How to generate `DebrisTypes` in the game: - Generate the total number of debris through `MaxDebris` and `MinDebris` first. - Traverse `DebrisTypes` and limit the quantity range through `DebrisMaximums` and `DebrisMinimums`. - When the number of generated debris will exceeds the total number, limit the quantity and end the traversal. - When the number of debris generated after a single traversal is not enough to exceed the total number, it will end if `DebrisTypes.Limit` is enabled, otherwise the traversal will restart like vanilla game do. ```
1 parent ff4e0aa commit 8bd666c

File tree

9 files changed

+191
-53
lines changed

9 files changed

+191
-53
lines changed

CREDITS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ This page lists all the individual contributions to the project by their author.
497497
- Fix an issue that the widespread damage caused by detonation on the bridge/ground cannot affect objects on the ground/bridge who are in the opposite case
498498
- Several new Infotypes, no display in specific status and a new single frame display method
499499
- Customizable spawn delay of `VoxelAnim`'s `TrailerAnim` and fix its incorrect position
500+
- Add `DebrisMinimums` to keep the count of debris within a certain range
500501
- **Ollerus**:
501502
- Build limit group enhancement
502503
- Customizable rocker amplitude

docs/Fixed-or-Improved-Logics.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ This page describes all ingame logics that are fixed or improved in Phobos witho
1414
- Fixed the bug when units are already dead but still in map (for sinking, crashing, dying animation, etc.), they could die again.
1515
- Fixed the bug when cloaked Desolator was unable to fire his deploy weapon.
1616
- Fixed the bug that temporaryed unit cannot be erased correctly and no longer raise an error.
17-
- Fixed `DebrisMaximums` (spawned debris type amounts cannot go beyond specified maximums anymore). Only applied when `DebrisMaximums` values amount is more than 1 for compatibility reasons.
1817
- Fixed building and defense tab hotkeys not enabling the placement mode after *Cannot build here.* triggered and the placement mode cancelled.
1918
- Fixed buildings with `UndeployInto` playing `EVA_NewRallypointEstablished` on undeploying.
2019
- Fixed buildings with `Naval=yes` ignoring `WaterBound=no` to be forced to place onto water.
@@ -1118,6 +1117,27 @@ DamagedSpeed=0.75 ; floating point value, multiplier
11181117
DamagedSpeed= ; floating point value, multiplier
11191118
```
11201119

1120+
### Debris voxel animations limitation
1121+
1122+
- Now, the original `DebrisMaximums` can be used in conjunction with new `DebrisMinimums` to limit the quantity of `DebrisTypes` when `DebrisTypes.Limit` is enabled.
1123+
- The default value of `DebrisTypes.Limit` is whether the number of `DebrisMaximums` is greater than (not equal to) 1 (for compatibility reasons).
1124+
1125+
In `rulesmd.ini`:
1126+
```ini
1127+
[SOMETECHNO] ; TechnoType
1128+
DebrisTypes.Limit= ; boolean
1129+
DebrisMaximums= ; List of integers
1130+
DebrisMinimums= ; List of integers
1131+
```
1132+
1133+
```{hint}
1134+
How to generate `DebrisTypes` in the game:
1135+
- Generate the total number of debris through `MaxDebris` and `MinDebris` first.
1136+
- Traverse `DebrisTypes` and limit the quantity range through `DebrisMaximums` and `DebrisMinimums`.
1137+
- When the number of generated debris will exceeds the total number, limit the quantity and end the traversal.
1138+
- When the number of debris generated after a single traversal is not enough to exceed the total number, it will end if `DebrisTypes.Limit` is enabled, otherwise the traversal will restart like vanilla game do.
1139+
```
1140+
11211141
### Exploding object customizations
11221142

11231143
- By default `Explodes=true` TechnoTypes have all of their passengers killed when they are destroyed. This behaviour can now be disabled by setting `Explodes.KillPassengers=false`.
@@ -1858,6 +1878,19 @@ DebrisAnims= ; List of AnimationTypes
18581878
Debris.Conventional=false ; boolean
18591879
```
18601880

1881+
### Custom debris voxel animations limitation
1882+
1883+
- Now, the original `DebrisMaximums` can be used in conjunction with new `DebrisMinimums` to limit the quantity of `DebrisTypes` when `DebrisTypes.Limit` is enabled.
1884+
- The default value of `DebrisTypes.Limit` is whether the number of `DebrisMaximums` is greater than (not equal to) 1 (for compatibility reasons).
1885+
1886+
In `rulesmd.ini`:
1887+
```ini
1888+
[SOMEWARHEAD] ; WarheadType
1889+
DebrisTypes.Limit= ; boolean
1890+
DebrisMaximums= ; List of integers
1891+
DebrisMinimums= ; List of integers
1892+
```
1893+
18611894
### Customizable rocker amplitude
18621895

18631896
- The rocker amplitude of warheads with `Rocker=yes` used to be determined by `Damage` value of the weapon. You can now override it with fixed value and add a multiplier to it.

docs/Whats-New.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ New:
406406
- [Display banner](AI-Scripting-and-Mapping.md#display-banner) (by Morton & ststl)
407407
- Allows refineries to use multiple ActiveAnim simultaneously (by TaranDahl)
408408
- Electric/RadBeam trail for laser tails (by NetsuNegi)
409+
- Add `DebrisMinimums` to keep the count of debris within a certain range (by CrimRecya)
409410
410411
Vanilla fixes:
411412
- Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya)

src/Ext/Bullet/Hooks.DetonateLogics.cpp

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <Ext/Techno/Body.h>
99
#include <Utilities/Helpers.Alex.h>
1010

11+
#include <VoxelAnimClass.h>
1112
#include <AircraftClass.h>
1213
#include <TacticalClass.h>
1314

@@ -152,45 +153,81 @@ DEFINE_HOOK(0x4690C1, BulletClass_Logics_DetonateOnAllMapObjects, 0x8)
152153

153154
#pragma endregion
154155

155-
DEFINE_HOOK(0x469D1A, BulletClass_Logics_Debris_Checks, 0x6)
156+
DEFINE_HOOK(0x469D1A, BulletClass_Logics_Debris, 0x6)
156157
{
157-
enum { SkipGameCode = 0x469EBA, SetDebrisCount = 0x469D36 };
158+
enum { SkipGameCode = 0x469EBA };
158159

159160
GET(BulletClass*, pThis, ESI);
160161

161-
bool isLand = pThis->GetCell()->LandType != LandType::Water;
162+
const auto pWH = pThis->WH;
163+
const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH);
162164

163-
if (!isLand && WarheadTypeExt::ExtMap.Find(pThis->WH)->Debris_Conventional)
165+
if (pWHExt->Debris_Conventional && pThis->GetCell()->LandType == LandType::Water)
164166
return SkipGameCode;
165167

166168
// Fix the debris count to be in range of Min, Max instead of Min, Max-1.
167-
R->EBX(ScenarioClass::Instance->Random.RandomRanged(pThis->WH->MinDebris, pThis->WH->MaxDebris));
169+
int totalSpawnAmount = ScenarioClass::Instance->Random.RandomRanged(pWH->MinDebris, pWH->MaxDebris);
168170

169-
return SetDebrisCount;
170-
}
171+
if (totalSpawnAmount > 0)
172+
{
173+
const auto& debrisTypes = pWH->DebrisTypes;
174+
const auto& debrisMaximums = pWH->DebrisMaximums;
171175

172-
DEFINE_HOOK(0x469E34, BulletClass_Logics_DebrisAnims, 0x5)
173-
{
174-
enum { SkipGameCode = 0x469EBA };
176+
const auto pOwner = pThis->Owner ? pThis->Owner->Owner : BulletExt::ExtMap.Find(pThis)->FirerHouse;
177+
auto coord = pThis->GetCoords();
175178

176-
GET(BulletClass*, pThis, ESI);
177-
GET(int, debrisCount, EBX);
179+
int count = Math::min(debrisTypes.Count, debrisMaximums.Count);
178180

179-
auto const pWHExt = WarheadTypeExt::ExtMap.Find(pThis->WH);
180-
auto const debrisAnims = pWHExt->DebrisAnims.GetElements(RulesClass::Instance->MetallicDebris);
181+
// Restore DebrisMaximums logic
182+
// Make DebrisTypes generate completely in accordance with DebrisMaximums,
183+
// without continuously looping until it exceeds totalSpawnAmount
184+
if (count > 0)
185+
{
186+
const auto& debrisMinimums = pWHExt->DebrisMinimums;
187+
const bool limit = pWHExt->DebrisTypes_Limit.Get(count > 1);
188+
const int minIndex = static_cast<int>(debrisMinimums.size()) - 1;
189+
int currentIndex = 0;
181190

182-
if (debrisAnims.size() < 1)
183-
return SkipGameCode;
191+
while (totalSpawnAmount > 0)
192+
{
193+
const int currentMaxDebris = Math::min(1, debrisMaximums[currentIndex]);
194+
const int currentMinDebris = (minIndex >= 0) ? Math::max(0, debrisMinimums[Math::min(currentIndex, minIndex)]) : 0;
195+
int amountToSpawn = Math::min(totalSpawnAmount, ScenarioClass::Instance->Random.RandomRanged(currentMinDebris, currentMaxDebris));
196+
totalSpawnAmount -= amountToSpawn;
184197

185-
while (debrisCount > 0)
186-
{
187-
int debrisIndex = ScenarioClass::Instance->Random.RandomRanged(0, debrisAnims.size() - 1);
188-
auto const pAnim = GameCreate<AnimClass>(debrisAnims[debrisIndex], pThis->GetCoords());
198+
for ( ; amountToSpawn > 0; --amountToSpawn)
199+
GameCreate<VoxelAnimClass>(debrisTypes[currentIndex], &coord, pOwner);
200+
201+
if (totalSpawnAmount <= 0)
202+
break;
189203

190-
if (pThis->Owner)
191-
AnimExt::SetAnimOwnerHouseKind(pAnim, pThis->Owner->Owner, nullptr, false, true);
204+
if (++currentIndex < count)
205+
continue;
206+
207+
if (limit)
208+
break;
209+
210+
currentIndex = 0;
211+
}
212+
}
213+
// Record the ownership of the animation
214+
// Different from technos, the original judging condition here were mutually exclusive
215+
else
216+
{
217+
const auto debrisAnims = pWHExt->DebrisAnims.GetElements(RulesClass::Instance->MetallicDebris);
218+
const int maxIndex = static_cast<int>(debrisAnims.size()) - 1;
192219

193-
debrisCount--;
220+
if (maxIndex >= 0)
221+
{
222+
do
223+
{
224+
int debrisIndex = ScenarioClass::Instance->Random.RandomRanged(0, maxIndex);
225+
const auto pAnim = GameCreate<AnimClass>(debrisAnims[debrisIndex], coord);
226+
AnimExt::SetAnimOwnerHouseKind(pAnim, pOwner, nullptr, false, true);
227+
}
228+
while (--totalSpawnAmount > 0);
229+
}
230+
}
194231
}
195232

196233
return SkipGameCode;

src/Ext/TechnoType/Body.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,9 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
693693

694694
this->EngineerRepairAmount.Read(exINI, pSection, "EngineerRepairAmount");
695695

696+
this->DebrisTypes_Limit.Read(exINI, pSection, "DebrisTypes.Limit");
697+
this->DebrisMinimums.Read(exINI, pSection, "DebrisMinimums");
698+
696699
// Ares 0.2
697700
this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius");
698701

@@ -1283,6 +1286,9 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm)
12831286
.Process(this->FireUp_ResetInRetarget)
12841287
//.Process(this->SecondaryFire)
12851288

1289+
.Process(this->DebrisTypes_Limit)
1290+
.Process(this->DebrisMinimums)
1291+
12861292
.Process(this->EngineerRepairAmount)
12871293
;
12881294
}

src/Ext/TechnoType/Body.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,9 @@ class TechnoTypeExt
378378
Valueable<bool> FireUp_ResetInRetarget;
379379
//Nullable<int> SecondaryFire;
380380

381+
Nullable<bool> DebrisTypes_Limit;
382+
ValueableVector<int> DebrisMinimums;
383+
381384
Valueable<int> EngineerRepairAmount;
382385

383386
ExtData(TechnoTypeClass* OwnerObject) : Extension<TechnoTypeClass>(OwnerObject)
@@ -708,6 +711,9 @@ class TechnoTypeExt
708711
, FireUp_ResetInRetarget { true }
709712
//, SecondaryFire {}
710713

714+
, DebrisTypes_Limit {}
715+
, DebrisMinimums {}
716+
711717
, EngineerRepairAmount { 0 }
712718
{ }
713719

src/Ext/WarheadType/Body.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
235235
this->AllowDamageOnSelf.Read(exINI, pSection, "AllowDamageOnSelf");
236236
this->DebrisAnims.Read(exINI, pSection, "DebrisAnims");
237237
this->Debris_Conventional.Read(exINI, pSection, "Debris.Conventional");
238+
this->DebrisTypes_Limit.Read(exINI, pSection, "DebrisTypes.Limit");
239+
this->DebrisMinimums.Read(exINI, pSection, "DebrisMinimums");
238240

239241
this->DetonateOnAllMapObjects.Read(exINI, pSection, "DetonateOnAllMapObjects");
240242
this->DetonateOnAllMapObjects_Full.Read(exINI, pSection, "DetonateOnAllMapObjects.Full");
@@ -490,6 +492,8 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm)
490492
.Process(this->AllowDamageOnSelf)
491493
.Process(this->DebrisAnims)
492494
.Process(this->Debris_Conventional)
495+
.Process(this->DebrisTypes_Limit)
496+
.Process(this->DebrisMinimums)
493497

494498
.Process(this->DetonateOnAllMapObjects)
495499
.Process(this->DetonateOnAllMapObjects_Full)

src/Ext/WarheadType/Body.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ class WarheadTypeExt
125125
Valueable<bool> AllowDamageOnSelf;
126126
NullableVector<AnimTypeClass*> DebrisAnims;
127127
Valueable<bool> Debris_Conventional;
128+
Nullable<bool> DebrisTypes_Limit;
129+
ValueableVector<int> DebrisMinimums;
128130

129131
Valueable<bool> DetonateOnAllMapObjects;
130132
Valueable<bool> DetonateOnAllMapObjects_Full;
@@ -310,6 +312,8 @@ class WarheadTypeExt
310312
, AllowDamageOnSelf { false }
311313
, DebrisAnims {}
312314
, Debris_Conventional { false }
315+
, DebrisTypes_Limit {}
316+
, DebrisMinimums {}
313317

314318
, DetonateOnAllMapObjects { false }
315319
, DetonateOnAllMapObjects_Full { true }

src/Misc/Hooks.BugFixes.cpp

Lines changed: 75 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -121,58 +121,104 @@ DEFINE_HOOK(0x737D57, UnitClass_ReceiveDamage_DyingFix, 0x7)
121121

122122
// Restore DebrisMaximums logic (issue #109)
123123
// Author: Otamaa
124-
DEFINE_HOOK(0x702299, TechnoClass_ReceiveDamage_DebrisMaximumsFix, 0xA)
124+
// Jun20,2025 Modified by: CrimRecya
125+
DEFINE_HOOK(0x702299, TechnoClass_ReceiveDamage_Debris, 0xA)
125126
{
127+
enum { SkipGameCode = 0x702572 };
128+
126129
GET(TechnoClass* const, pThis, ESI);
127130

128131
const auto pType = pThis->GetTechnoType();
129132

130-
// Jun12,2025 - CrimRecya : I think there is no need to return to the unreasonable vanilla logic
131-
// Otherwise, they should be in a parallel relationship rather than a sequential relationship
132-
/* // If DebrisMaximums has one value, then legacy behavior is used
133-
if (pType->DebrisMaximums.Count == 1 &&
134-
pType->DebrisMaximums.GetItem(0) > 0 &&
135-
pType->DebrisTypes.Count > 0)
136-
{
137-
return 0;
138-
}*/
139-
140-
// Removed -1 from the MaxDebris
133+
// Fix the debris count to be in range of Min, Max instead of Min, Max-1.
141134
int totalSpawnAmount = ScenarioClass::Instance->Random.RandomRanged(pType->MinDebris, pType->MaxDebris);
142135

143-
const auto& debrisTypes = pType->DebrisTypes;
144-
const auto& debrisMaximums = pType->DebrisMaximums;
145-
146-
// Make DebrisTypes generate completely in accordance with DebrisMaximums,
147-
// without continuously looping until it exceeds totalSpawnAmount
148-
if (debrisTypes.Count > 0 && debrisMaximums.Count > 0)
136+
if (totalSpawnAmount > 0)
149137
{
138+
const auto& debrisTypes = pType->DebrisTypes;
139+
const auto& debrisMaximums = pType->DebrisMaximums;
140+
141+
const auto pOwner = pThis->Owner;
150142
auto coord = pThis->GetCoords();
151143

152-
for (int currentIndex = 0; currentIndex < debrisMaximums.Count; ++currentIndex)
144+
int count = Math::min(debrisTypes.Count, debrisMaximums.Count);
145+
146+
// Restore DebrisMaximums logic
147+
// Make DebrisTypes generate completely in accordance with DebrisMaximums,
148+
// without continuously looping until it exceeds totalSpawnAmount
149+
if (count > 0)
153150
{
154-
const int currentMaxDebris = debrisMaximums.GetItem(currentIndex);
151+
const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType);
152+
const auto& debrisMinimums = pTypeExt->DebrisMinimums;
153+
const bool limit = pTypeExt->DebrisTypes_Limit.Get(count > 1);
154+
const int minIndex = static_cast<int>(debrisMinimums.size()) - 1;
155+
int currentIndex = 0;
155156

156-
if (currentMaxDebris > 0)
157+
while (totalSpawnAmount > 0)
157158
{
158-
const int adjustedMaximum = Math::min(currentMaxDebris, pType->MaxDebris);
159-
int amountToSpawn = std::abs(ScenarioClass::Instance->Random.Random()) % (adjustedMaximum + 1); // 0x702337
160-
amountToSpawn = Math::min(amountToSpawn, totalSpawnAmount);
159+
const int currentMaxDebris = Math::min(1, debrisMaximums[currentIndex]);
160+
const int currentMinDebris = (minIndex >= 0) ? Math::max(0, debrisMinimums[Math::min(currentIndex, minIndex)]) : 0;
161+
int amountToSpawn = Math::min(totalSpawnAmount, ScenarioClass::Instance->Random.RandomRanged(currentMinDebris, currentMaxDebris));
161162
totalSpawnAmount -= amountToSpawn;
162163

163164
for ( ; amountToSpawn > 0; --amountToSpawn)
164-
{
165-
GameCreate<VoxelAnimClass>(debrisTypes.GetItem(currentIndex), &coord, pThis->Owner);
166-
}
165+
GameCreate<VoxelAnimClass>(debrisTypes[currentIndex], &coord, pOwner);
167166

168167
if (totalSpawnAmount <= 0)
168+
return SkipGameCode;
169+
170+
if (++currentIndex < count)
171+
continue;
172+
173+
if (limit)
169174
break;
175+
176+
currentIndex = 0;
177+
}
178+
}
179+
180+
// Record the ownership of the animation
181+
// The vanilla game will consume all totalSpawnAmount when DebrisTypes exists, so there
182+
// will be no situation where DebrisTypes and DebrisAnims are generated simultaneously.
183+
// However, after fixing DebrisMinimums, according to the original judgment conditions,
184+
// it is possible to generate both DebrisTypes and DebrisAnims simultaneously. They all
185+
// need to be set up to take effect, and it doesn't seem to bring any further problems,
186+
// so I think this can be retained. ( 0x7023E5 / 0x702402 / 0x7024BB )
187+
{
188+
const auto& debrisAnims = pType->DebrisAnims;
189+
const int maxIndex = debrisAnims.Count - 1;
190+
191+
if (maxIndex >= 0)
192+
{
193+
do
194+
{
195+
int debrisIndex = ScenarioClass::Instance->Random.RandomRanged(0, maxIndex);
196+
const auto pAnim = GameCreate<AnimClass>(debrisAnims[debrisIndex], coord);
197+
AnimExt::SetAnimOwnerHouseKind(pAnim, pOwner, nullptr, false, true);
198+
}
199+
while (--totalSpawnAmount > 0);
200+
}
201+
}
202+
203+
if (count <= 0)
204+
{
205+
const auto& metallicDebrisAnims = RulesClass::Instance->MetallicDebris;
206+
const int maxMetallicIndex = metallicDebrisAnims.Count - 1;
207+
208+
if (maxMetallicIndex >= 0)
209+
{
210+
do
211+
{
212+
int debrisIndex = ScenarioClass::Instance->Random.RandomRanged(0, maxMetallicIndex);
213+
const auto pAnim = GameCreate<AnimClass>(metallicDebrisAnims[debrisIndex], coord);
214+
AnimExt::SetAnimOwnerHouseKind(pAnim, pOwner, nullptr, false, true);
215+
}
216+
while (--totalSpawnAmount > 0);
170217
}
171218
}
172219
}
173220

174-
R->EBX(totalSpawnAmount);
175-
return 0x7023E5;
221+
return SkipGameCode;
176222
}
177223

178224
// issue #250: Building placement hotkey not responding

0 commit comments

Comments
 (0)