Skip to content
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ This page lists all the individual contributions to the project by their author.
- Fix the bug that Locomotor warhead won't stop working when firer (except for vehicle) stop firing
- Fix the bug that hover vehicle will sink if destroyed on bridge
- Customize squid grapple animation
- Penetrates damage on transporter
- **Apollo** - Translucent SHP drawing patches
- **ststl**:
- Customizable `ShowTimer` priority of superweapons
Expand Down
30 changes: 30 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -2366,6 +2366,36 @@ In `rulesmd.ini`:
RemoveParasite= ; boolean
```

### Penetrates damage on transporter

- Warheads can now damage passenger on impact.
- If `PenetratesTransport.Level` of warhead larger than `PenetratesTransport.Level` of target and it's passengers, it will enable penetrates damage logic on passenger.
- `PenetratesTransport.PassThrough` is the chance of penetration, actual chance will multiply by `PenetratesTransport.PassThroughMultiplier` of target.
- `PenetratesTransport.FatalRate` is the chance of one hit kill passenger, actual change will multiply by `PenetratesTransport.FatalRateMultiplier` of target.
- `PenetratesTransport.DamageAll` control whether it will damage all passengers or random one passenger in transport.
- `PenetratesTransport.DamageMultiplier` is multiplier of damage on passenger.
- `PenetratesTransport.CleanSound` will play when all passengers has been killed.

In `rulesmd.ini`
```ini
[SOMEWARHEAD] ; WarheadType
PenetratesTransport.Level=0 ; integer
PenetratesTransport.PassThrough=1.0 ; double
PenetratesTransport.FatalRate=0.0 ; double
PenetratesTransport.DamageMultiplier=1.0 ; double
PenetratesTransport.DamageAll=false ; boolean
PenetratesTransport.CleanSound= ; sound entry

[SOMETECHNO] ; TechnoType
PenetratesTransport.Level= ; integer
PenetratesTransport.PassThroughMultiplier=1.0 ; double
PenetratesTransport.FatalRateMultiplier=1.0 ; double
PenetratesTransport.DamageMultiplier=1.0 ; double

[CombatDamage]
PenetratesTransport.Level=10 ; integer, default value of technotype's penetrate level
```

### Remove disguise on impact

- Warheads can now remove disguise from disguised spies or mirage tanks. This will work even if the disguised was acquired by default through `PermaDisguise`.
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ New:
- [Units can customize the attack voice that plays when using more weapons](New-or-Enhanced-Logics.md#multi-voiceattack) (by FlyStar)
- Customize squid grapple animation (by NetsuNegi)
- [Auto deploy for GI-like infantry](Fixed-or-Improved-Logics.md#auto-deploy-for-gi-like-infantry) (by TaranDahl)
- Penetrates damage on transporter (by NetsuNegi)

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/Rules/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,8 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI)
this->AttackMove_StopWhenTargetAcquired.Read(exINI, GameStrings::General, "AttackMove.StopWhenTargetAcquired");

this->Parasite_GrappleAnim.Read(exINI, GameStrings::AudioVisual, "Parasite.GrappleAnim");

this->PenetratesTransport_Level.Read(exINI, GameStrings::CombatDamage, "PenetratesTransport.Level");

this->AINormalTargetingDelay.Read(exINI, GameStrings::General, "AINormalTargetingDelay");
this->PlayerNormalTargetingDelay.Read(exINI, GameStrings::General, "PlayerNormalTargetingDelay");
Expand Down Expand Up @@ -554,6 +556,7 @@ void RulesExt::ExtData::Serialize(T& Stm)
.Process(this->AttackMove_StopWhenTargetAcquired)
.Process(this->Parasite_GrappleAnim)
.Process(this->InfantryAutoDeploy)
.Process(this->PenetratesTransport_Level)
;
}

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 @@ -246,6 +246,8 @@ class RulesExt
Nullable<bool> AttackMove_StopWhenTargetAcquired;

NullableIdx<AnimTypeClass> Parasite_GrappleAnim;

Valueable<int> PenetratesTransport_Level;

// cache tint color
int TintColorIronCurtain;
Expand Down Expand Up @@ -449,7 +451,10 @@ class RulesExt
, AttackMove_StopWhenTargetAcquired { }

, Parasite_GrappleAnim {}

, InfantryAutoDeploy { false }

, PenetratesTransport_Level { 10 }
{ }

virtual ~ExtData() = default;
Expand Down
34 changes: 34 additions & 0 deletions src/Ext/Techno/Hooks.ReceiveDamage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,15 @@ DEFINE_HOOK(0x702672, TechnoClass_ReceiveDamage_RevengeWeapon, 0x5)
return 0;
}

DEFINE_HOOK(0x518434, InfantryClass_ReceiveDamage_SkipDeathAnim, 0x7)
{
enum { SkipDeathAnim = 0x5185F1 };

GET(InfantryClass*, pThis, ESI);

return pThis->Transporter ? SkipDeathAnim : 0;
}

// Issue #237 NotHuman additional animations support
// Author: Otamaa
DEFINE_HOOK(0x518505, InfantryClass_ReceiveDamage_NotHuman, 0x4)
Expand Down Expand Up @@ -393,3 +402,28 @@ DEFINE_HOOK(0x701E18, TechnoClass_ReceiveDamage_ReflectDamage, 0x7)

return 0;
}

DEFINE_HOOK(0x702823, TechnoClass_ReceiveDamage_SkipDamagedParticle, 0x7)
{
enum { SkipParticle = 0x702A25, RemoveParticle = 0x70283C, SpawnParticle = 0x702857 };

GET(TechnoClass*, pThis, ESI);

if (pThis->Transporter)
return SkipParticle;

return pThis->GetHealthPercentage() <= RulesClass::Instance->ConditionYellow ? SpawnParticle : RemoveParticle;
}

DEFINE_HOOK(0x737E6E, UnitClass_ReceiveDamage_SkipExplode, 0xA)
{
enum { ContinueCheck = 0x737E78, SkipExplode = 0x737F74 };

GET(UnitClass*, pThis, ESI);

if (pThis->Transporter)
return SkipExplode;

R->EAX(pThis->GetHeight());
return ContinueCheck;
}
12 changes: 11 additions & 1 deletion src/Ext/TechnoType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,12 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->AttackMove_StopWhenTargetAcquired.Read(exINI, pSection, "AttackMove.StopWhenTargetAcquired");
this->AttackMove_PursuitTarget.Read(exINI, pSection, "AttackMove.PursuitTarget");

this->InfantryAutoDeploy.Read(exINI, pSection, "InfantryAutoDeploy");
this->InfantryAutoDeploy.Read(exINI, pSection, "InfantryAutoDeploy");

this->PenetratesTransport_Level.Read(exINI, pSection, "PenetratesTransport.Level");
this->PenetratesTransport_PassThroughMultiplier.Read(exINI, pSection, "PenetratesTransport.PassThroughMultiplier");
this->PenetratesTransport_FatalRateMultiplier.Read(exINI, pSection, "PenetratesTransport.FatalRateMultiplier");
this->PenetratesTransport_DamageMultiplier.Read(exINI, pSection, "PenetratesTransport.DamageMultiplier");

// Ares 0.2
this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius");
Expand Down Expand Up @@ -1580,6 +1585,11 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm)
.Process(this->AttackMove_Follow_IfMindControlIsFull)
.Process(this->AttackMove_StopWhenTargetAcquired)
.Process(this->AttackMove_PursuitTarget)

.Process(this->PenetratesTransport_Level)
.Process(this->PenetratesTransport_PassThroughMultiplier)
.Process(this->PenetratesTransport_FatalRateMultiplier)
.Process(this->PenetratesTransport_DamageMultiplier)

.Process(this->MultiWeapon)
.Process(this->MultiWeapon_IsSecondary)
Expand Down
10 changes: 10 additions & 0 deletions src/Ext/TechnoType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,11 @@ class TechnoTypeExt
Nullable<bool> AttackMove_StopWhenTargetAcquired;
Valueable<bool> AttackMove_PursuitTarget;

Nullable<int> PenetratesTransport_Level;
Valueable<double> PenetratesTransport_PassThroughMultiplier;
Valueable<double> PenetratesTransport_FatalRateMultiplier;
Valueable<double> PenetratesTransport_DamageMultiplier;

Valueable<bool> MultiWeapon;
ValueableVector<bool> MultiWeapon_IsSecondary;
Valueable<int> MultiWeapon_SelectCount;
Expand Down Expand Up @@ -775,6 +780,11 @@ class TechnoTypeExt
, AttackMove_StopWhenTargetAcquired { }
, AttackMove_PursuitTarget { false }

, PenetratesTransport_Level {}
, PenetratesTransport_PassThroughMultiplier { 1.0 }
, PenetratesTransport_FatalRateMultiplier { 1.0 }
, PenetratesTransport_DamageMultiplier { 1.0 }

, MultiWeapon { false }
, MultiWeapon_IsSecondary {}
, MultiWeapon_SelectCount { 2 }
Expand Down
15 changes: 15 additions & 0 deletions src/Ext/WarheadType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,13 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
if (this->AffectsAbovePercent > this->AffectsBelowPercent)
Debug::Log("[Developer warning][%s] AffectsAbovePercent is bigger than AffectsBelowPercent, the warhead will never activate!\n", pSection);

this->PenetratesTransport_Level.Read(exINI, pSection, "PenetratesTransport.Level");
this->PenetratesTransport_PassThrough.Read(exINI, pSection, "PenetratesTransport.PassThrough");
this->PenetratesTransport_FatalRate.Read(exINI, pSection, "PenetratesTransport.FatalRate");
this->PenetratesTransport_DamageMultiplier.Read(exINI, pSection, "PenetratesTransport.DamageMultiplier");
this->PenetratesTransport_DamageAll.Read(exINI, pSection, "PenetratesTransport.DamageAll");
this->PenetratesTransport_CleanSound.Read(exINI, pSection, "PenetratesTransport.CleanSound");

// Convert.From & Convert.To
TypeConvertGroup::Parse(this->Convert_Pairs, exINI, pSection, AffectedHouse::All);

Expand Down Expand Up @@ -356,6 +363,7 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
|| this->AttachEffects.RemoveGroups.size() > 0
|| this->BuildingSell
|| this->BuildingUndeploy
|| this->PenetratesTransport_Level > 0
);

char tempBuffer[32];
Expand Down Expand Up @@ -528,6 +536,13 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm)
.Process(this->AffectsNeutral)
.Process(this->HealthCheck)

.Process(this->PenetratesTransport_Level)
.Process(this->PenetratesTransport_PassThrough)
.Process(this->PenetratesTransport_FatalRate)
.Process(this->PenetratesTransport_DamageMultiplier)
.Process(this->PenetratesTransport_DamageAll)
.Process(this->PenetratesTransport_CleanSound)

.Process(this->InflictLocomotor)
.Process(this->RemoveInflictedLocomotor)

Expand Down
17 changes: 16 additions & 1 deletion src/Ext/WarheadType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ class WarheadTypeExt
Valueable<double> AffectsAbovePercent;
Valueable<bool> AffectsNeutral;

Valueable<int> PenetratesTransport_Level;
Valueable<double> PenetratesTransport_PassThrough;
Valueable<double> PenetratesTransport_FatalRate;
Valueable<double> PenetratesTransport_DamageMultiplier;
Valueable<bool> PenetratesTransport_DamageAll;
ValueableIdx<VocClass> PenetratesTransport_CleanSound;

// Ares tags
// http://ares-developers.github.io/Ares-docs/new/warheads/general.html
Valueable<bool> AffectsEnemies;
Expand Down Expand Up @@ -373,6 +380,13 @@ class WarheadTypeExt
, AffectsAbovePercent { 0.0 }
, AffectsNeutral { true }

, PenetratesTransport_Level { 0 }
, PenetratesTransport_PassThrough { 1.0 }
, PenetratesTransport_FatalRate { 0.0 }
, PenetratesTransport_DamageMultiplier { 1.0 }
, PenetratesTransport_DamageAll { false }
, PenetratesTransport_CleanSound { -1 }

, AffectsEnemies { true }
, AffectsOwner {}
, EffectsRequireVerses { true }
Expand Down Expand Up @@ -426,13 +440,14 @@ class WarheadTypeExt
void InterceptBullets(TechnoClass* pOwner, BulletClass* pInterceptor, const CoordStruct& coords);
DamageAreaResult DamageAreaWithTarget(const CoordStruct& coords, int damage, TechnoClass* pSource, WarheadTypeClass* pWH, bool affectsTiberium, HouseClass* pSourceHouse, TechnoClass* pTarget);
private:
void DetonateOnOneUnit(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* pOwner = nullptr, bool bulletWasIntercepted = false);
void DetonateOnOneUnit(HouseClass* pHouse, TechnoClass* pTarget, const CoordStruct& coords, int damage, TechnoClass* pOwner = nullptr, bool bulletWasIntercepted = false);
void ApplyRemoveDisguise(HouseClass* pHouse, TechnoClass* pTarget);
void ApplyRemoveMindControl(TechnoClass* pTarget);
void ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* Owner);
void ApplyShieldModifiers(TechnoClass* pTarget);
void ApplyAttachEffects(TechnoClass* pTarget, HouseClass* pInvokerHouse, TechnoClass* pInvoker);
void ApplyBuildingUndeploy(TechnoClass* pTarget);
void ApplyPenetratesTransport(TechnoClass* pTarget, TechnoClass* pInvoker, HouseClass* pInvokerHouse, const CoordStruct& coords, int damage);
double GetCritChance(TechnoClass* pFirer) const;
};

Expand Down
Loading
Loading