Skip to content

Commit 11205db

Browse files
authored
MultiWeapon (#1695)
![multiweapons](https://github.com/user-attachments/assets/192f8aa9-baa2-4f9d-89c5-25f480f9d83e) You are free to decide whether to use Weapon x or not, instead of passively using Primary/secondary. 您可以自由决定是否启动WeaponX,而不是只能使用主/副武器。 - TechnoType reads `WeaponX` as their weapon when `MultiWeapon=yes`, be careful not to forget `WeaponCount`. `MultiWeapon=yes`时会读取`WeaponX`作为他们的武器,注意不要忘记了`WeaponCount`。 - `MultiWeapon.IsSecondary` can only be used by infantry, who will play the animation `SecondaryFire` when using these weapons. `MultiWeapon.IsSecondary`只能用于步兵,使用这些武器时步兵会播放`SecondaryFire`。 - `MultiWeapon.SelectCount` determines the number of weapons that can be selected. The lower the number the better the performance. The default is 2. `MultiWeapon.SelectCount`决定可选择的武器数量。数量越少性能越好。默认为 2。 In `rulesmd.ini`: ```ini [SOMETECHNO] ; TechnoType MultiWeapon= ; boolean MultiWeapon.IsSecondary= ; List of integers MultiWeapon.SelectCount= ; integer ```
1 parent 1be2d46 commit 11205db

File tree

11 files changed

+430
-13
lines changed

11 files changed

+430
-13
lines changed

CREDITS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ This page lists all the individual contributions to the project by their author.
373373
- Fix the issue where some units crashed after the deployment transformation
374374
- Turretless vehicles with `Voxel=no` support use `FireUp` like infantry
375375
- Infantry support `IsGattling=yes`
376+
- Support for more optional weapons
376377
- **NetsuNegi**:
377378
- Forbidding parallel AI queues by type
378379
- Jumpjet crash speed fix when crashing onto building

Phobos.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
</ItemGroup>
2020
<ItemGroup>
2121
<ClCompile Include="src\Ext\Infantry\Hooks.Firing.cpp" />
22+
<ClCompile Include="src\Ext\TechnoType\Hooks.MultiWeapon.cpp" />
2223
<ClCompile Include="src\Ext\Unit\Hooks.Firing.cpp" />
2324
<ClCompile Include="src\Ext\EBolt\Body.cpp" />
2425
<ClCompile Include="src\Ext\EBolt\Hooks.cpp" />

docs/New-or-Enhanced-Logics.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,6 +1604,26 @@ MindControlLink.VisibleToHouse=all ; Affected House Enumeration (none|owner/s
16041604
MultiMindControl.ReleaseVictim=false ; boolean
16051605
```
16061606

1607+
### Multi Weapon
1608+
1609+
![image](_static/images/multiweapons.gif)
1610+
*Multi Weapon used to release different weapons against different targets in **Zero Boundary** by @[Stormsulfur](https://space.bilibili.com/11638715/lists/5358986)*
1611+
1612+
- You can now use `WeaponX` to enable more than 2 weapons for a TechnoType without hardcoded `Gunner=yes`, `IsGattling=yes` or `IsChargeTurret=yes` restriction.
1613+
- Set `MultiWeapon=yes` to enable this feature, be careful not to forget `WeaponCount`.
1614+
- `MultiWeapon.IsSecondary` specifies which weapons will be considered as `Secondary` when selecting weapons or triggering infantry's `SecondaryFire` settings. If not set, `Weapon1` will be considered as `Secondary`.
1615+
- `MultiWeapon.SelectCount` determines the number of weapons that can be selected by default weapon selection logic. Notice that higher number is bad for performance.
1616+
- If the number is smaller than the total amount of weapons, the ones with smaller indices will be picked.
1617+
- Other weapons can still be used for logic that specify a weapon index, such as [ForceWeapon](#forcing-specific-weapon-against-certain-targets).
1618+
1619+
In `rulesmd.ini`:
1620+
```ini
1621+
[SOMETECHNO] ; TechnoType
1622+
MultiWeapon=false ; boolean
1623+
MultiWeapon.IsSecondary= ; List of integers
1624+
MultiWeapon.SelectCount=2 ; integer
1625+
```
1626+
16071627
### No Manual Move
16081628

16091629
- You can now specify whether a TechnoType is unable to receive move command.

docs/Whats-New.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -406,12 +406,10 @@ New:
406406
- [Display banner](AI-Scripting-and-Mapping.md#display-banner) (by Morton & ststl)
407407
- [Allows refineries to use multiple ActiveAnim simultaneously](Fixed-or-Improved-Logics.md#allows-refineries-to-use-multiple-activeanim-simultaneously) (by TaranDahl)
408408
- Electric/RadBeam trail for laser tails (by NetsuNegi)
409-
<<<<<<< HEAD
410-
- Ground line for select box (by NetsuNegi)
411-
=======
412409
- Add `DebrisMinimums` to keep the count of debris within a certain range (by CrimRecya)
413410
- Several attackmove related enhancement (by TaranDahl)
414-
>>>>>>> upstream/develop
411+
- Ground line for select box (by NetsuNegi)
412+
- Support for more optional weapons (by FlyStar)
415413
416414
Vanilla fixes:
417415
- Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya)

docs/_static/images/multiweapons.gif

3.65 MB
Loading

src/Ext/Techno/Body.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,4 +263,5 @@ class TechnoExt
263263
static int GetWeaponIndexAgainstWall(TechnoClass* pThis, OverlayTypeClass* pWallOverlayType);
264264
static void ApplyKillWeapon(TechnoClass* pThis, TechnoClass* pSource, WarheadTypeClass* pWH);
265265
static void ApplyRevengeWeapon(TechnoClass* pThis, TechnoClass* pSource, WarheadTypeClass* pWH);
266+
static bool MultiWeaponCanFire(TechnoClass* const pThis, AbstractClass* const pTarget, WeaponTypeClass* const pWeaponType);
266267
};

src/Ext/Techno/Hooks.Firing.cpp

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ DEFINE_HOOK(0x6F3339, TechnoClass_WhatWeaponShouldIUse_Interceptor, 0x8)
3838
return SkipGameCode;
3939
}
4040

41+
DEFINE_HOOK(0x6F3360, TechnoClass_WhatWeaponShouldIUse_MultiWeapon, 0x6)
42+
{
43+
GET(TechnoTypeClass*, pType, EAX);
44+
enum { SkipGameCode = 0x6F3379 };
45+
46+
if (TechnoTypeExt::ExtMap.Find(pType)->MultiWeapon.Get()
47+
&& (pType->WhatAmI() != AbstractType::UnitType || !pType->Gunner))
48+
{
49+
return SkipGameCode;
50+
}
51+
52+
return 0;
53+
}
54+
4155
DEFINE_HOOK(0x6F33CD, TechnoClass_WhatWeaponShouldIUse_ForceFire, 0x6)
4256
{
4357
enum { ReturnWeaponIndex = 0x6F37AF };
@@ -94,6 +108,15 @@ DEFINE_HOOK(0x6F3428, TechnoClass_WhatWeaponShouldIUse_ForceWeapon, 0x6)
94108
return UseWeaponIndex;
95109
}
96110

111+
// Multi weapon
112+
const int multiWeaponIndex = pTypeExt->SelectMultiWeapon(pThis, pTarget);
113+
114+
if (multiWeaponIndex >= 0)
115+
{
116+
R->EAX(multiWeaponIndex);
117+
return UseWeaponIndex;
118+
}
119+
97120
return 0;
98121
}
99122

@@ -253,14 +276,10 @@ DEFINE_HOOK(0x6FC339, TechnoClass_CanFire, 0x6)
253276
// Checking for nullptr is not required here, since the game has already executed them before calling the hook -- Belonit
254277
const auto pWH = pWeapon->Warhead;
255278
const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH);
279+
const int nMoney = pWHExt->TransactMoney;
256280

257-
if (pWHExt)
258-
{
259-
const int nMoney = pWHExt->TransactMoney;
260-
261-
if (nMoney < 0 && pThis->Owner->Available_Money() < -nMoney)
262-
return CannotFire;
263-
}
281+
if (nMoney < 0 && pThis->Owner->Available_Money() < -nMoney)
282+
return CannotFire;
264283

265284
// AAOnly doesn't need to be checked if LandTargeting=1.
266285
if (pThis->GetTechnoType()->LandTargeting != LandTargetingType::Land_Not_OK && pWeapon->Projectile->AA
@@ -314,7 +333,7 @@ DEFINE_HOOK(0x6FC339, TechnoClass_CanFire, 0x6)
314333

315334
if (pWH->Airstrike)
316335
{
317-
if (!pWHExt || !EnumFunctions::IsTechnoEligible(pTargetTechno, pWHExt->AirstrikeTargets))
336+
if (!EnumFunctions::IsTechnoEligible(pTargetTechno, pWHExt->AirstrikeTargets))
318337
return CannotFire;
319338

320339
if (!TechnoExt::ExtMap.Find(pTargetTechno)->TypeExtData->AllowAirstrike.Get(pTargetTechno->AbstractFlags & AbstractFlags::Foot ? true : static_cast<BuildingClass*>(pTargetTechno)->Type->CanC4))

src/Ext/Techno/WeaponHelpers.cpp

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,3 +350,148 @@ int TechnoExt::ExtData::ApplyForceWeaponInRange(AbstractClass* pTarget)
350350

351351
return forceWeaponIndex;
352352
}
353+
354+
bool TechnoExt::MultiWeaponCanFire(TechnoClass* const pThis, AbstractClass* const pTarget, WeaponTypeClass* const pWeaponType)
355+
{
356+
if (!pWeaponType || pWeaponType->NeverUse
357+
|| (pThis->InOpenToppedTransport && !pWeaponType->FireInTransport))
358+
{
359+
return false;
360+
}
361+
362+
const auto rtti = pTarget->WhatAmI();
363+
const bool isBuilding = rtti == AbstractType::Building;
364+
const auto pWH = pWeaponType->Warhead;
365+
const auto pBulletType = pWeaponType->Projectile;
366+
367+
const auto pTechno = abstract_cast<TechnoClass*, true>(pTarget);
368+
const bool isInAir = pTechno ? pTechno->IsInAir() : false;
369+
370+
const auto pOwner = pThis->Owner;
371+
const auto pTechnoOwner = pTechno ? pTechno->Owner : nullptr;
372+
const bool isAllies = pTechnoOwner ? pOwner->IsAlliedWith(pTechnoOwner) : false;
373+
374+
if (isInAir)
375+
{
376+
if (!pBulletType->AA)
377+
return false;
378+
}
379+
else
380+
{
381+
if (BulletTypeExt::ExtMap.Find(pBulletType)->AAOnly.Get())
382+
{
383+
return false;
384+
}
385+
else if (pWH->ElectricAssault)
386+
{
387+
if (!isBuilding || !isAllies
388+
|| !static_cast<BuildingClass*>(pTarget)->Type->Overpowerable)
389+
{
390+
return false;
391+
}
392+
}
393+
else if (pWH->IsLocomotor)
394+
{
395+
if (isBuilding)
396+
return false;
397+
}
398+
}
399+
400+
CellClass* pTargetCell = nullptr;
401+
402+
// Ignore target cell for airborne target technos.
403+
if (!pTechno || !isInAir)
404+
{
405+
if (auto const pObject = abstract_cast<ObjectClass*, true>(pTarget))
406+
pTargetCell = pObject->GetCell();
407+
else if (auto const pCell = abstract_cast<CellClass*, true>(pTarget))
408+
pTargetCell = pCell;
409+
}
410+
411+
const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeaponType);
412+
413+
if (!pWeaponExt->SkipWeaponPicking)
414+
{
415+
if (pTargetCell && !EnumFunctions::IsCellEligible(pTargetCell, pWeaponExt->CanTarget, true, true))
416+
return false;
417+
418+
if (pTechno)
419+
{
420+
if (!EnumFunctions::IsTechnoEligible(pTechno, pWeaponExt->CanTarget)
421+
|| !EnumFunctions::CanTargetHouse(pWeaponExt->CanTargetHouses, pOwner, pTechnoOwner)
422+
|| !pWeaponExt->IsHealthRatioEligible(pTechno)
423+
|| !pWeaponExt->HasRequiredAttachedEffects(pTechno, pThis))
424+
{
425+
return false;
426+
}
427+
}
428+
}
429+
430+
if (pTechno)
431+
{
432+
if (pTechno->AttachedBomb ? pWH->IvanBomb : pWH->BombDisarm)
433+
return false;
434+
435+
if (!pWH->Temporal && pTechno->BeingWarpedOut)
436+
return false;
437+
438+
if (pWH->Parasite
439+
&& (isBuilding || static_cast<FootClass*>(pTechno)->ParasiteEatingMe))
440+
{
441+
return false;
442+
}
443+
444+
const auto pTechnoType = pTechno->GetTechnoType();
445+
446+
if (pWH->MindControl
447+
&& (pTechnoType->ImmuneToPsionics || pTechno->IsMindControlled() || pOwner == pTechnoOwner))
448+
{
449+
return false;
450+
}
451+
452+
if (pWeaponType->DrainWeapon
453+
&& (!pTechnoType->Drainable || pTechno->DrainingMe || isAllies))
454+
{
455+
return false;
456+
}
457+
458+
if (pWH->Airstrike)
459+
{
460+
if (!EnumFunctions::IsTechnoEligible(pTechno, WarheadTypeExt::ExtMap.Find(pWH)->AirstrikeTargets))
461+
return false;
462+
463+
const auto pTechnoTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType);
464+
465+
if (pTechno->AbstractFlags & AbstractFlags::Foot)
466+
{
467+
if (!pTechnoTypeExt->AllowAirstrike.Get(true))
468+
return false;
469+
}
470+
else if (pTechnoTypeExt->AllowAirstrike.Get(static_cast<BuildingTypeClass*>(pTechnoType)->CanC4)
471+
&& (!pTechnoType->ResourceDestination || !pTechnoType->ResourceGatherer))
472+
{
473+
return false;
474+
}
475+
}
476+
477+
if (GeneralUtils::GetWarheadVersusArmor(pWH, pTechno, pTechnoType) == 0.0)
478+
return false;
479+
}
480+
else if (rtti == AbstractType::Cell)
481+
{
482+
if (pTargetCell->OverlayTypeIndex >= 0)
483+
{
484+
const auto pOverlayType = OverlayTypeClass::Array.GetItem(pTargetCell->OverlayTypeIndex);
485+
486+
if (pOverlayType->Wall && !pWH->Wall && (!pWH->Wood || pOverlayType->Armor != Armor::Wood))
487+
return false;
488+
}
489+
}
490+
else if (rtti == AbstractType::Terrain)
491+
{
492+
if (!pWH->Wood)
493+
return false;
494+
}
495+
496+
return true;
497+
}

0 commit comments

Comments
 (0)